diff --git a/README.md b/README.md index 9178ab451..9caac40dd 100644 --- a/README.md +++ b/README.md @@ -448,6 +448,8 @@ The following is a list of static resources. - [azure_subscriptions](docs/resources/azure_subscriptions.md) - [azure_synapse_notebook](docs/resources/azure_synapse_notebook.md) - [azure_synapse_notebooks](docs/resources/azure_synapse_notebooks.md) +- [azure_synapse_workspace](docs/resources/azure_synapse_workspace.md) +- [azure_synapse_workspaces](docs/resources/azure_synapse_workspaces.md) - [azure_virtual_machine](docs/resources/azure_virtual_machine.md) - [azure_virtual_machines](docs/resources/azure_virtual_machines.md) - [azure_virtual_machine_disk](docs/resources/azure_virtual_machine_disk.md) diff --git a/docs-chef-io/content/inspec/resources/azure_synapse_workspace.md b/docs-chef-io/content/inspec/resources/azure_synapse_workspace.md new file mode 100644 index 000000000..a7f274b03 --- /dev/null +++ b/docs-chef-io/content/inspec/resources/azure_synapse_workspace.md @@ -0,0 +1,103 @@ ++++ +title = "azure_synapse_workspace Resource" +platform = "azure" +draft = false +gh_repo = "inspec-azure" + +[menu.inspec] +title = "azure_synapse_workspace" +identifier = "inspec/resources/azure/azure_synapse_workspace Resource" +parent = "inspec/resources/azure" ++++ + +Use the `azure_synapse_workspace` InSpec audit resource to test properties related to an Azure Synapse workspace. + +## Azure REST API Version, Endpoint, and HTTP Client Parameters + +{{% inspec_azure_common_parameters %}} + +## Installation + +{{% inspec_azure_install %}} + +## Syntax + +```ruby +describe azure_synapse_workspace(resource_group: 'RESOURCE_GROUP', name: 'SYNAPSE_WORKSPACE_NAME') do + it { should exist } + its('type') { should eq 'Microsoft.SqlVirtualMachine/sqlVirtualMachineGroups' } + its('location') { should eq 'eastus' } +end +``` + +```ruby +describe azure_synapse_workspace(resource_group: 'RESOURCE_GROUP', name: 'SYNAPSE_WORKSPACE_NAME') do + it { should exist } +end +``` + +## Parameters + +`name` _(required)_ +: Name of the Azure Synapse workspace to test. + +`resource_group` _(required)_ +: Azure resource group that the targeted resource resides in. + +## Properties + +`id` +: Resource Id. + +`name` +: Resource name. + +`type` +: Resource type. `Microsoft.Synapse/workspaces`. + +`location` +: The Geo-location where the resource lives. + +`properties` +: The properties of the Synapse workspaces. + +`properties.provisioningState` +: State of the resource. + + +For properties applicable to all resources, such as `type`, `name`, `id`, `properties`, refer to [`azure_generic_resource`]({{< relref "azure_generic_resource.md#properties" >}}). + +Also, refer to [Azure documentation](https://docs.microsoft.com/en-us/rest/api/synapse/workspaces/get) for other properties available. + +## Examples + +**Test that the Synapse workspaces is provisioned successfully.** + +```ruby +describe azure_synapse_workspace(resource_group: 'RESOURCE_GROUP', name: 'SYNAPSE_WORKSPACE_NAME') do + its('properties.provisioningState') { should eq 'Succeeded' } +end +``` + +## Matchers + +{{% inspec_matchers_link %}} + +### exists + +```ruby +# If a Synapse workspaces is found it will exist + +describe azure_synapse_workspace(resource_group: 'RESOURCE_GROUP', name: 'SYNAPSE_WORKSPACE_NAME') do + it { should exist } +end +# if Synapse workspaces is not found it will not exist + +describe azure_synapse_workspace(resource_group: 'RESOURCE_GROUP', name: 'SYNAPSE_WORKSPACE_NAME') do + it { should_not exist } +end +``` + +## Azure Permissions + +{{% azure_permissions_service_principal role="reader" %}} diff --git a/docs-chef-io/content/inspec/resources/azure_synapse_workspaces.md b/docs-chef-io/content/inspec/resources/azure_synapse_workspaces.md new file mode 100644 index 000000000..6909b50ac --- /dev/null +++ b/docs-chef-io/content/inspec/resources/azure_synapse_workspaces.md @@ -0,0 +1,113 @@ ++++ +title = "azure_synapse_workspaces Resource" +platform = "azure" +draft = false +gh_repo = "inspec-azure" + +[menu.inspec] +title = "azure_synapse_workspaces" +identifier = "inspec/resources/azure/azure_synapse_workspaces Resource" +parent = "inspec/resources/azure" ++++ + +Use the `azure_synapse_workspaces` InSpec audit resource to test properties related to all Azure Synapse workspaces. + +## Azure REST API Version, Endpoint, and HTTP Client Parameters + +{{% inspec_azure_common_parameters %}} + +## Installation + +{{% inspec_azure_install %}} + +## Syntax + +An `azure_synapse_workspaces` resource block returns all Azure Synapse workspaces. + +```ruby +describe azure_synapse_workspaces do + #... +end +``` + +## Parameters + +`resource_group` _(optional)_ +: Azure resource group that the targeted resource resides in. + +## Properties + +`ids` +: A list of resource IDs. + +: **Field**: `id` + +`names` +: A list of resource Names. + +: **Field**: `name` + +`types` +: A list of the resource types. + +: **Field**: `type` + +`properties` +: A list of Properties for all the Synapse workspaces. + +: **Field**: `properties` + +`locations` +: A list of the Geo-locations. + +: **Field**: `location` + +`provisioningStates` +: A list of provisioning states of the Synapse workspaces. + +: **Field**: `provisioningState` + +{{% inspec_filter_table %}} + +## Examples + +**Loop through Synapse workspaces by their names.** + +```ruby +azure_synapse_workspaces.names.each do |name| + describe azure_synapse_workspace(resource_group: 'RESOURCE_GROUP', name: name) do + it { should exist } + end +end +``` + +**Test that there are Synapse workspaces that are successfully provisioned.** + +```ruby +describe azure_synapse_workspaces(resource_group: 'RESOURCE_GROUP').where(provisioningState: 'Succeeded') do + it { should exist } +end +``` + +## Matchers + +{{% inspec_matchers_link %}} + +### exists + +```ruby +# Should not exist if no Synapse workspaces are present + +describe azure_synapse_workspaces(resource_group: 'RESOURCE_GROUP') do + it { should_not exist } +end +# Should exist if the filter returns at least one Synapse workspaces + +describe azure_synapse_workspaces(resource_group: 'RESOURCE_GROUP') do + it { should exist } +end +``` + +## Azure Permissions + +{{% azure_permissions_service_principal role="reader" %}} diff --git a/libraries/azure_synapse_workspace.rb b/libraries/azure_synapse_workspace.rb new file mode 100644 index 000000000..16aab084b --- /dev/null +++ b/libraries/azure_synapse_workspace.rb @@ -0,0 +1,22 @@ +require 'azure_generic_resource' + +class AzureSynapseWorkspace < AzureGenericResource + name 'azure_synapse_workspace' + desc 'Retrieves and verifies the settings of an Azure Synapse Workspace.' + example <<-EXAMPLE + describe azure_synapse_workspace(resource_group: 'inspec-def-rg', name: 'synapse-ws') do + it { should exist } + end + EXAMPLE + + def initialize(opts = {}) + raise ArgumentError, 'Parameters must be provided in an Hash object.' unless opts.is_a?(Hash) + + opts[:resource_provider] = specific_resource_constraint('Microsoft.Synapse/workspaces', opts) + super(opts, true) + end + + def to_s + super(AzureSynapseWorkspace) + end +end diff --git a/libraries/azure_synapse_workspaces.rb b/libraries/azure_synapse_workspaces.rb new file mode 100644 index 000000000..772851731 --- /dev/null +++ b/libraries/azure_synapse_workspaces.rb @@ -0,0 +1,35 @@ +require 'azure_generic_resources' + +class AzureSynapseWorkspaces < AzureGenericResources + name 'azure_synapse_workspaces' + desc 'Verifies settings for a collection of Azure Synapse Workspaces' + example <<-EXAMPLE + describe azure_synapse_workspaces do + it { should exist } + end + EXAMPLE + + def initialize(opts = {}) + raise ArgumentError, 'Parameters must be provided in an Hash object.' unless opts.is_a?(Hash) + + opts[:resource_provider] = specific_resource_constraint('Microsoft.Synapse/workspaces', opts) + super(opts, true) + return if failed_resource? + + populate_filter_table_from_response + end + + def to_s + super(AzureSynapseWorkspaces) + end + + private + + def populate_table + @resources.each do |resource| + @table << resource.merge(resource[:properties]) + .merge(resource.dig(:properties, :defaultDataLakeStorage)) + .merge(resource.dig(:properties, :connectivityEndpoints)) + end + end +end diff --git a/terraform/azure.tf b/terraform/azure.tf index e4945a1e2..c0a27ead3 100644 --- a/terraform/azure.tf +++ b/terraform/azure.tf @@ -191,9 +191,7 @@ resource "azurerm_key_vault_key" "vk" { resource "azurerm_managed_disk" "disk" { name = var.encrypted_disk_name resource_group_name = azurerm_resource_group.rg.name - location = var.location - storage_account_type = var.managed_disk_type create_option = "Empty" disk_size_gb = 1 @@ -895,7 +893,6 @@ resource "azurerm_iothub" "iothub" { capacity = 1 } - endpoint { type = "AzureIotHub.EventHub" connection_string = azurerm_eventhub_authorization_rule.auth_rule_inspectesteh.primary_connection_string @@ -904,7 +901,6 @@ resource "azurerm_iothub" "iothub" { max_chunk_size_in_bytes = 314572800 } - route { name = "ExampleRoute" source = "DeviceLifecycleEvents" @@ -1115,7 +1111,6 @@ resource "azurerm_api_management" "apim01" { XML - } } resource "azurerm_stream_analytics_job" "streaming_job" { @@ -1139,7 +1134,6 @@ resource "azurerm_stream_analytics_job" "streaming_job" { INTO [YourOutputAlias] FROM [YourInputAlias] QUERY - } resource "azurer_stream_analytics_function_javascript_udf" "streaming_job_function" { @@ -1335,7 +1329,6 @@ resource "azurerm_policy_definition" "inspec_policy_definition" { } } PARAMETERS - } resource "azurerm_policy_assignment" "inspec_compliance_policy_assignment" { @@ -1557,7 +1550,6 @@ resource "azurerm_subnet_route_table_association" "route_table_assoc_inspec" { route_table_id = azurerm_route_table.route_table_sql_instance_inspec.id } - resource "azurerm_sql_managed_instance" "sql_instance_for_inspec" { name = "sql-instance-for-inspec" resource_group_name = azurerm_resource_group.rg.name @@ -1634,7 +1626,6 @@ resource "azurerm_servicebus_topic" "inspec_sb_topic" { name = "inspec-servicebus-topic" resource_group_name = azurerm_resource_group.rg.name namespace_name = azurerm_servicebus_namespace.sb.name - enable_partitioning = true } @@ -1684,4 +1675,23 @@ resource "azurerm_managed_application" "mng_app" { storageAccountNamePrefix = "storeNamePrefix" storageAccountType = "Standard_LRS" } +} + +resource "azurerm_synapse_workspace" "synapse_inspec_ws" { + name = "synapse-inspec-ws" + resource_group_name = azurerm_resource_group.rg.name + location = azurerm_resource_group.rg.location + storage_data_lake_gen2_filesystem_id = azurerm_storage_data_lake_gen2_filesystem.inspec_adls_gen2.id + sql_administrator_login = "sqladminuser" + sql_administrator_login_password = "H@Sh1CoR3!" + + aad_admin { + login = "AzureAD Admin" + object_id = "00000000-0000-0000-0000-000000000000" + tenant_id = "00000000-0000-0000-0000-000000000000" + } + + tags = { + Env = "inspec" + } } \ No newline at end of file diff --git a/terraform/outputs.tf b/terraform/outputs.tf index 95df9023c..fe40cc654 100644 --- a/terraform/outputs.tf +++ b/terraform/outputs.tf @@ -574,4 +574,9 @@ output "service_bus_subscription_rule_name" { output "inspec_managed_app" { description = "The Managed Application Name" value = azurerm_managed_application.mng_app.name +} + +output "synapse_inspec_ws_name" { + description = "Synapse Workspace Name" + value = azurerm_synapse_workspace.synapse_inspec_ws.name } \ No newline at end of file diff --git a/test/integration/verify/controls/azure_synapse_workspace.rb b/test/integration/verify/controls/azure_synapse_workspace.rb new file mode 100644 index 000000000..95a34430f --- /dev/null +++ b/test/integration/verify/controls/azure_synapse_workspace.rb @@ -0,0 +1,14 @@ +resource_group = input(:resource_group, value: '') +location = input(:location, value: '') +synapse_ws_name = input(:synapse_inspec_ws_name, value: '') + +control 'Verify settings of all Azure Synapse Workspaces' do + describe azure_synapse_workspace(resource_group: resource_group, name: synapse_ws_name) do + it { should exist } + its('name') { should eq synapse_ws_name } + its('location') { should eq location.downcase.gsub("\s", '') } + its('properties.provisioningState') { should eq 'Succeeded' } + its('properties.managedResourceGroupName') { should eq resource_group } + its('properties.sqlAdministratorLogin') { should eq 'login' } + end +end diff --git a/test/integration/verify/controls/azure_synapse_workspaces.rb b/test/integration/verify/controls/azure_synapse_workspaces.rb new file mode 100644 index 000000000..0524a3f40 --- /dev/null +++ b/test/integration/verify/controls/azure_synapse_workspaces.rb @@ -0,0 +1,14 @@ +resource_group = input(:resource_group, value: '') +location = input(:location, value: '') +synapse_ws_name = input(:synapse_inspec_ws_name, value: '') + +control 'Verify settings of all Azure Synapse Workspaces' do + describe azure_synapse_workspaces do + it { should exist } + its('names') { should include synapse_ws_name } + its('locations') { should include location.downcase.gsub("\s", '') } + its('provisioningStates') { should include 'Succeeded' } + its('managedResourceGroupNames') { should include resource_group } + its('sqlAdministratorLogins') { should include 'login' } + end +end diff --git a/test/unit/resources/azure_synapse_workspace_test.rb b/test/unit/resources/azure_synapse_workspace_test.rb new file mode 100644 index 000000000..363ed161d --- /dev/null +++ b/test/unit/resources/azure_synapse_workspace_test.rb @@ -0,0 +1,17 @@ +require_relative 'helper' +require 'azure_synapse_workspace' + +class AzureSynapseWorkspaceConstructorTest < Minitest::Test + def test_empty_param_not_ok + assert_raises(ArgumentError) { AzureSynapseWorkspace.new } + end + + # resource_provider should not be allowed. + def test_resource_provider_not_ok + assert_raises(ArgumentError) { AzureSynapseWorkspace.new(resource_provider: 'some_type') } + end + + def test_resource_group_name_alone_not_ok + assert_raises(ArgumentError) { AzureSynapseWorkspace.new(resource_group: 'test') } + end +end diff --git a/test/unit/resources/azure_synapse_workspaces_test.rb b/test/unit/resources/azure_synapse_workspaces_test.rb new file mode 100644 index 000000000..ab3c80881 --- /dev/null +++ b/test/unit/resources/azure_synapse_workspaces_test.rb @@ -0,0 +1,21 @@ +require_relative 'helper' +require 'azure_synapse_workspaces' + +class AzureSynapseWorkspacesConstructorTest < Minitest::Test + # resource_type should not be allowed. + def test_resource_type_not_ok + assert_raises(ArgumentError) { AzureSynapseWorkspaces.new(resource_provider: 'some_type') } + end + + def tag_value_not_ok + assert_raises(ArgumentError) { AzureSynapseWorkspaces.new(tag_value: 'some_tag_value') } + end + + def tag_name_not_ok + assert_raises(ArgumentError) { AzureSynapseWorkspaces.new(tag_name: 'some_tag_name') } + end + + def test_name_not_ok + assert_raises(ArgumentError) { AzureSynapseWorkspaces.new(name: 'some_name') } + end +end