diff --git a/README.md b/README.md index 022782429..896121174 100644 --- a/README.md +++ b/README.md @@ -342,6 +342,8 @@ The following is a list of static resources. - [azure_load_balancers](docs/resources/azure_load_balancers.md) - [azure_lock](docs/resources/azure_lock.md) - [azure_locks](docs/resources/azure_locks.md) +- [azure_managed_application](docs/resources/azure_managed_application.md) +- [azure_managed_applications](docs/resources/azure_managed_applications.md) - [azure_management_group](docs/resources/azure_management_group.md) - [azure_management_groups](docs/resources/azure_management_groups.md) - [azure_mariadb_server](docs/resources/azure_mariadb_server.md) diff --git a/docs-chef-io/content/inspec/resources/azure_managed_application.md b/docs-chef-io/content/inspec/resources/azure_managed_application.md new file mode 100644 index 000000000..0594b2316 --- /dev/null +++ b/docs-chef-io/content/inspec/resources/azure_managed_application.md @@ -0,0 +1,111 @@ ++++ +title = "azure_managed_application Resource" +platform = "azure" +draft = false +gh_repo = "inspec-azure" + +[menu.inspec] +title = "azure_managed_application" +identifier = "inspec/resources/azure/azure_managed_application Resource" +parent = "inspec/resources/azure" ++++ + +Use the `azure_managed_application` InSpec audit resource to test properties related to an Azure managed application. + +## Azure REST API Version, Endpoint, and HTTP Client Parameters + +{{% inspec_azure_common_parameters %}} + +## Installation + +{{% inspec_azure_install %}} + +## Syntax + +`name`, `resource_group` is a required parameter. + +```ruby +describe azure_managed_application(resource_group: 'RESOURCE_GROUP', name: 'MANAGED_APPLICATION_NAME') do + it { should exist } + its('type') { should eq 'Microsoft.ServiceBus/Namespaces' } + its('location') { should eq 'East US' } +end +``` + +```ruby +describe azure_managed_application(resource_group: 'RESOURCE_GROUP', name: 'MANAGED_APPLICATION_NAME') do + it { should exist } +end +``` + +## Parameters + +`name` _(required)_ +: Name of the Azure managed applications 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.Solutions/applications`. + +`location` +: Resource location. + +`properties` +: The properties of the managed application. + +`properties.plan` +: The plan information. + +`properties.identity` +: The identity of the resource. + +`properties.provisioningState` +: Provisioning state of the namespace. + + +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/managedapplications/applications/get) for other properties available. + +## Examples + +**Test that the managed applications is provisioned successfully.** + +```ruby +describe azure_managed_application(resource_group: 'RESOURCE_GROUP', name: 'MANAGED_APPLICATION_NAME') do + its('properties.provisioningState') { should eq 'Succeeded' } +end +``` + +## Matchers + +{{% inspec_matchers_link %}} + +### exists + +```ruby +# If a managed application is found it will exist + +describe azure_managed_application(resource_group: 'RESOURCE_GROUP', name: 'MANAGED_APPLICATION_NAME') do + it { should exist } +end +# if managed application is not found it will not exist + +describe azure_managed_application(resource_group: 'RESOURCE_GROUP', name: 'MANAGED_APPLICATION_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_managed_applications.md b/docs-chef-io/content/inspec/resources/azure_managed_applications.md new file mode 100644 index 000000000..7dd6e394d --- /dev/null +++ b/docs-chef-io/content/inspec/resources/azure_managed_applications.md @@ -0,0 +1,128 @@ ++++ +title = "azure_managed_applications Resource" +platform = "azure" +draft = false +gh_repo = "inspec-azure" + +[menu.inspec] +title = "azure_managed_applications" +identifier = "inspec/resources/azure/azure_managed_applications Resource" +parent = "inspec/resources/azure" ++++ + +Use the `azure_managed_applications` InSpec audit resource to test properties related to all Azure managed applications. + +## Azure REST API Version, Endpoint, and HTTP Client Parameters + +{{% inspec_azure_common_parameters %}} + +## Installation + +{{% inspec_azure_install %}} + +## Syntax + +An `azure_managed_applications` resource block returns all Azure managed applications. + +```ruby +describe azure_managed_applications 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 managed applications. + +: **Field**: `properties` + +`locations` +: A list of the resource locations. + +: **Field**: `location` + +`identities` +: A list of the identity of the resources. + +: **Field**: `identity` + +`plans` +: A list of the plan informations. + +: **Field**: `plan` + +`provisioningStates` +: A list of provisioning states of the app. + +: **Field**: `provisioningState` + +`publisherTenantIds` +: A list of the publisher tenant Id. + +: **Field**: `publisherTenantId` + +{{% inspec_filter_table %}} + +## Examples + +**Loop through managed applications by their names.** + +```ruby +azure_managed_applications(resource_group: 'RESOURCE_GROUP').names.each do |name| + describe azure_managed_application(resource_group: 'RESOURCE_GROUP', name: name) do + it { should exist } + end +end +``` + +**Test that there are managed applications that are successfully provisioned.** + +```ruby +describe azure_managed_applications(resource_group: 'RESOURCE_GROUP').where(provisioningState: 'Succeeded') do + it { should exist } +end +``` + +## Matchers + +{{% inspec_matchers_link %}} + +### exists + +```ruby +# Should not exist if no managed applications are present + +describe azure_managed_applications(resource_group: 'RESOURCE_GROUP') do + it { should_not exist } +end +# Should exist if the filter returns at least one managed applications + +describe azure_managed_applications(resource_group: 'RESOURCE_GROUP') do + it { should exist } +end +``` + +## Azure Permissions + +{{% azure_permissions_service_principal role="reader" %}} diff --git a/libraries/azure_managed_application.rb b/libraries/azure_managed_application.rb new file mode 100644 index 000000000..d5e114b16 --- /dev/null +++ b/libraries/azure_managed_application.rb @@ -0,0 +1,22 @@ +require 'azure_generic_resource' + +class AzureManagedApplication < AzureGenericResource + name 'azure_managed_application' + desc 'Retrieves and verifies the settings of an Azure Managed Application.' + example <<-EXAMPLE + describe azure_managed_application(resource_group: 'inspec-rg', name: 'app_name') 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.Solutions/applications', opts) + super(opts, true) + end + + def to_s + super(AzureManagedApplication) + end +end diff --git a/libraries/azure_managed_applications.rb b/libraries/azure_managed_applications.rb new file mode 100644 index 000000000..466185534 --- /dev/null +++ b/libraries/azure_managed_applications.rb @@ -0,0 +1,33 @@ +require 'azure_generic_resources' + +class AzureManagedApplications < AzureGenericResources + name 'azure_managed_applications' + desc 'Verifies settings for a collection of Azure Managed Applications.' + example <<-EXAMPLE + describe azure_managed_applications 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.Solutions/applications', opts) + super(opts, true) + return if failed_resource? + + populate_filter_table_from_response + end + + def to_s + super(AzureManagedApplications) + end + + private + + def populate_table + @resources.each do |resource| + @table << resource.merge(resource[:properties]) + end + end +end diff --git a/terraform/azure.tf b/terraform/azure.tf index 4126a3747..e4945a1e2 100644 --- a/terraform/azure.tf +++ b/terraform/azure.tf @@ -1654,4 +1654,34 @@ resource "azurerm_servicebus_subscription_rule" "inspec-sub-rule" { subscription_name = azurerm_servicebus_subscription.inspec-sub.name filter_type = "SqlFilter" sql_filter = "colour = 'red'" +} + +resource "azurerm_managed_application_definition" "mng_app_def" { + name = "inspecmngappdef" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + lock_level = "ReadOnly" + package_file_uri = "https://github.com/Azure/azure-managedapp-samples/raw/master/Managed Application Sample Packages/201-managed-storage-account/managedstorage.zip" + display_name = "InspecManagedAppDefinition" + description = "Test Managed App Definition for Inspec" + + authorization { + service_principal_id = data.azurerm_client_config.current.object_id + role_definition_id = split("/", data.azurerm_role_definition.contributor.id)[length(split("/", data.azurerm_role_definition.contributor.id)) - 1] + } +} + +resource "azurerm_managed_application" "mng_app" { + name = "inspectestmngapp" + location = azurerm_resource_group.rg.location + resource_group_name = azurerm_resource_group.rg.name + kind = "ServiceCatalog" + managed_resource_group_name = "InspecGroup" + application_definition_id = azurerm_managed_application_definition.mng_app_def.id + + parameters = { + location = azurerm_resource_group.rg.location + storageAccountNamePrefix = "storeNamePrefix" + storageAccountType = "Standard_LRS" + } } \ No newline at end of file diff --git a/terraform/outputs.tf b/terraform/outputs.tf index 775130c80..95df9023c 100644 --- a/terraform/outputs.tf +++ b/terraform/outputs.tf @@ -569,4 +569,9 @@ output "service_bus_subscription_name" { output "service_bus_subscription_rule_name" { description = "The name of the Azure Service Bus Subscription Rule" value = azurerm_servicebus_subscription_rule.inspec-sub-rule.name +} + +output "inspec_managed_app" { + description = "The Managed Application Name" + value = azurerm_managed_application.mng_app.name } \ No newline at end of file diff --git a/test/integration/verify/controls/azure_managed_application.rb b/test/integration/verify/controls/azure_managed_application.rb new file mode 100644 index 000000000..b1cc0630c --- /dev/null +++ b/test/integration/verify/controls/azure_managed_application.rb @@ -0,0 +1,11 @@ +resource_group = input(:resource_group, value: '') +inspec_managed_app = input(:inspec_managed_app, value: '') + +control 'test the properties of an Azure Managed APP' do + describe azure_managed_application(resource_group: resource_group, name: inspec_managed_app) do + it { should exist } + its('kind') { should eq 'ServiceCatalog' } + its('location') { should eq location } + its('properties.provisioningState') { should eq 'Created' } + end +end diff --git a/test/integration/verify/controls/azure_managed_applications.rb b/test/integration/verify/controls/azure_managed_applications.rb new file mode 100644 index 000000000..dcbb797ba --- /dev/null +++ b/test/integration/verify/controls/azure_managed_applications.rb @@ -0,0 +1,14 @@ +resource_group = input(:resource_group, value: '') +inspec_managed_app = input(:inspec_managed_app, value: '') +location = input(:location, value: '') + +control 'test the properties of all Azure Service Bus Namespaces' do + describe azure_managed_applications(resource_group: resource_group) do + it { should exist } + its('names') { should include inspec_managed_app } + its('managementModes') { should include 'Managed' } + its('locations') { should include location } + its('types') { should include 'Microsoft.Solutions/applications' } + its('provisioningStates') { should include 'Created' } + end +end diff --git a/test/unit/resources/azure_managed_application_test.rb b/test/unit/resources/azure_managed_application_test.rb new file mode 100644 index 000000000..9ea1fcdd8 --- /dev/null +++ b/test/unit/resources/azure_managed_application_test.rb @@ -0,0 +1,17 @@ +require_relative 'helper' +require 'azure_managed_application' + +class AzureManagedApplicationConstructorTest < Minitest::Test + def test_empty_param_not_ok + assert_raises(ArgumentError) { AzureManagedApplication.new } + end + + # resource_provider should not be allowed. + def test_resource_provider_not_ok + assert_raises(ArgumentError) { AzureManagedApplication.new(resource_provider: 'some_type') } + end + + def test_resource_group_name_alone_not_ok + assert_raises(ArgumentError) { AzureManagedApplication.new(resource_group: 'test') } + end +end diff --git a/test/unit/resources/azure_managed_applications_test.rb b/test/unit/resources/azure_managed_applications_test.rb new file mode 100644 index 000000000..722fd77cc --- /dev/null +++ b/test/unit/resources/azure_managed_applications_test.rb @@ -0,0 +1,21 @@ +require_relative 'helper' +require 'azure_managed_applications' + +class AzureManagedApplicationsConstructorTest < Minitest::Test + # resource_type should not be allowed. + def test_resource_type_not_ok + assert_raises(ArgumentError) { AzureManagedApplications.new(resource_provider: 'some_type') } + end + + def tag_value_not_ok + assert_raises(ArgumentError) { AzureManagedApplications.new(tag_value: 'some_tag_value') } + end + + def tag_name_not_ok + assert_raises(ArgumentError) { AzureManagedApplications.new(tag_name: 'some_tag_name') } + end + + def test_name_not_ok + assert_raises(ArgumentError) { AzureManagedApplications.new(name: 'some_name') } + end +end