diff --git a/internal/services/policy/policy.go b/internal/services/policy/policy.go index 7fefa678e6f7..f5e2ee3cdc30 100644 --- a/internal/services/policy/policy.go +++ b/internal/services/policy/policy.go @@ -10,14 +10,19 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/utils" ) -func getPolicyDefinitionByDisplayName(ctx context.Context, client *policy.DefinitionsClient, displayName, managementGroupName string) (policy.Definition, error) { +func getPolicyDefinitionByDisplayName(ctx context.Context, client *policy.DefinitionsClient, displayName, managementGroupName string, + builtInOnly bool) (policy.Definition, error) { var policyDefinitions policy.DefinitionListResultIterator var err error if managementGroupName != "" { policyDefinitions, err = client.ListByManagementGroupComplete(ctx, managementGroupName, "", nil) } else { - policyDefinitions, err = client.ListComplete(ctx, "", nil) + if builtInOnly { + policyDefinitions, err = client.ListBuiltInComplete(ctx, "", nil) + } else { + policyDefinitions, err = client.ListComplete(ctx, "", nil) + } } if err != nil { return policy.Definition{}, fmt.Errorf("loading Policy Definition List: %+v", err) diff --git a/internal/services/policy/policy_definition_built_in_data_source.go b/internal/services/policy/policy_definition_built_in_data_source.go new file mode 100644 index 000000000000..0e9ed58d4c86 --- /dev/null +++ b/internal/services/policy/policy_definition_built_in_data_source.go @@ -0,0 +1,20 @@ +package policy + +import ( + "time" + + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +// dataSourceArmPolicyDefinitionBuiltIn read built-in policy definition only +func dataSourceArmPolicyDefinitionBuiltIn() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: policyDefinitionReadFunc(true), + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: policyDefinitionDataSourceSchema(), + } +} diff --git a/internal/services/policy/policy_definition_built_in_data_source_test.go b/internal/services/policy/policy_definition_built_in_data_source_test.go new file mode 100644 index 000000000000..801b5dd4165d --- /dev/null +++ b/internal/services/policy/policy_definition_built_in_data_source_test.go @@ -0,0 +1,42 @@ +package policy_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type PolicyDefinitionBuiltInDataSource struct{} + +func TestAccDataSourceAzureRMPolicyDefinitionBuiltIn_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_policy_definition_built_in", "test") + d := PolicyDefinitionBuiltInDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: d.basic("Allowed resource types"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("id").HasValue("/providers/Microsoft.Authorization/policyDefinitions/a08ec900-254a-4555-9bf5-e42af04b5c5c"), + check.That(data.ResourceName).Key("name").HasValue("a08ec900-254a-4555-9bf5-e42af04b5c5c"), + check.That(data.ResourceName).Key("display_name").HasValue("Allowed resource types"), + check.That(data.ResourceName).Key("type").HasValue("Microsoft.Authorization/policyDefinitions"), + check.That(data.ResourceName).Key("description").HasValue("This policy enables you to specify the resource types that your organization can deploy. Only resource types that support 'tags' and 'location' will be affected by this policy. To restrict all resources please duplicate this policy and change the 'mode' to 'All'."), + check.That(data.ResourceName).Key("mode").HasValue("Indexed"), + ), + }, + }) +} + +func (d PolicyDefinitionBuiltInDataSource) basic(name string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +data "azurerm_policy_definition_built_in" "test" { + display_name = "%s" +} +`, name) +} diff --git a/internal/services/policy/policy_definition_data_source.go b/internal/services/policy/policy_definition_data_source.go index 6d42227f76d9..e6d552d4fca2 100644 --- a/internal/services/policy/policy_definition_data_source.go +++ b/internal/services/policy/policy_definition_data_source.go @@ -14,139 +14,149 @@ import ( func dataSourceArmPolicyDefinition() *pluginsdk.Resource { return &pluginsdk.Resource{ - Read: dataSourceArmPolicyDefinitionRead, + Read: policyDefinitionReadFunc(false), Timeouts: &pluginsdk.ResourceTimeout{ Read: pluginsdk.DefaultTimeout(5 * time.Minute), }, - Schema: map[string]*pluginsdk.Schema{ - "display_name": { - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringIsNotEmpty, - ExactlyOneOf: []string{"name", "display_name"}, - }, + Schema: policyDefinitionDataSourceSchema(), + } +} - "name": { - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringIsNotEmpty, - ExactlyOneOf: []string{"name", "display_name"}, - }, +func policyDefinitionDataSourceSchema() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "display_name": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringIsNotEmpty, + ExactlyOneOf: []string{"name", "display_name"}, + }, - "management_group_name": { - Type: pluginsdk.TypeString, - Optional: true, - }, + "name": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringIsNotEmpty, + ExactlyOneOf: []string{"name", "display_name"}, + }, - "type": { - Type: pluginsdk.TypeString, - Computed: true, - }, + "management_group_name": { + Type: pluginsdk.TypeString, + Optional: true, + }, - "description": { - Type: pluginsdk.TypeString, - Computed: true, - }, + "type": { + Type: pluginsdk.TypeString, + Computed: true, + }, - "policy_type": { - Type: pluginsdk.TypeString, - Computed: true, - }, + "description": { + Type: pluginsdk.TypeString, + Computed: true, + }, - "policy_rule": { - Type: pluginsdk.TypeString, - Computed: true, - }, + "policy_type": { + Type: pluginsdk.TypeString, + Computed: true, + }, - "parameters": { - Type: pluginsdk.TypeString, - Computed: true, - }, + "policy_rule": { + Type: pluginsdk.TypeString, + Computed: true, + }, - "metadata": { - Type: pluginsdk.TypeString, - Computed: true, - }, + "parameters": { + Type: pluginsdk.TypeString, + Computed: true, + }, - "role_definition_ids": { - Type: pluginsdk.TypeList, - Computed: true, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, - }, - }, + "metadata": { + Type: pluginsdk.TypeString, + Computed: true, + }, - "mode": { - Type: pluginsdk.TypeString, - Computed: true, + "role_definition_ids": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, }, }, + + "mode": { + Type: pluginsdk.TypeString, + Computed: true, + }, } } -func dataSourceArmPolicyDefinitionRead(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).Policy.DefinitionsClient - ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) - defer cancel() - - displayName := d.Get("display_name").(string) - name := d.Get("name").(string) - managementGroupName := "" - if v, ok := d.GetOk("management_group_name"); ok { - managementGroupName = v.(string) - } +func policyDefinitionReadFunc(builtInOnly bool) func(d *pluginsdk.ResourceData, meta interface{}) error { + return func(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Policy.DefinitionsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + displayName := d.Get("display_name").(string) + name := d.Get("name").(string) + managementGroupName := "" + if v, ok := d.GetOk("management_group_name"); ok { + managementGroupName = v.(string) + } - var policyDefinition policy.Definition - var err error - // one of display_name and name must be non-empty, this is guaranteed by schema - if displayName != "" { - policyDefinition, err = getPolicyDefinitionByDisplayName(ctx, client, displayName, managementGroupName) - if err != nil { - return fmt.Errorf("reading Policy Definition (Display Name %q): %+v", displayName, err) + var policyDefinition policy.Definition + var err error + // one of display_name and name must be non-empty, this is guaranteed by schema + if displayName != "" { + policyDefinition, err = getPolicyDefinitionByDisplayName(ctx, client, displayName, managementGroupName, builtInOnly) + if err != nil { + return fmt.Errorf("reading Policy Definition (Display Name %q): %+v", displayName, err) + } } - } - if name != "" { - policyDefinition, err = getPolicyDefinitionByName(ctx, client, name, managementGroupName) + if name != "" { + if builtInOnly && managementGroupName == "" { + policyDefinition, err = client.GetBuiltIn(ctx, name) + } else { + policyDefinition, err = getPolicyDefinitionByName(ctx, client, name, managementGroupName) + } + if err != nil { + return fmt.Errorf("reading Policy Definition %q: %+v", name, err) + } + } + + id, err := parse.PolicyDefinitionID(*policyDefinition.ID) if err != nil { - return fmt.Errorf("reading Policy Definition %q: %+v", name, err) + return fmt.Errorf("parsing Policy Definition %q: %+v", *policyDefinition.ID, err) } - } - id, err := parse.PolicyDefinitionID(*policyDefinition.ID) - if err != nil { - return fmt.Errorf("parsing Policy Definition %q: %+v", *policyDefinition.ID, err) - } + d.SetId(id.Id) + d.Set("name", policyDefinition.Name) + d.Set("display_name", policyDefinition.DisplayName) + d.Set("description", policyDefinition.Description) + d.Set("type", policyDefinition.Type) + d.Set("policy_type", policyDefinition.PolicyType) + d.Set("mode", policyDefinition.Mode) + + policyRule := policyDefinition.PolicyRule.(map[string]interface{}) + if policyRuleStr := flattenJSON(policyRule); policyRuleStr != "" { + d.Set("policy_rule", policyRuleStr) + roleIDs, _ := getPolicyRoleDefinitionIDs(policyRuleStr) + d.Set("role_definition_ids", roleIDs) + } else { + return fmt.Errorf("flattening Policy Definition Rule %q: %+v", name, err) + } - d.SetId(id.Id) - d.Set("name", policyDefinition.Name) - d.Set("display_name", policyDefinition.DisplayName) - d.Set("description", policyDefinition.Description) - d.Set("type", policyDefinition.Type) - d.Set("policy_type", policyDefinition.PolicyType) - d.Set("mode", policyDefinition.Mode) - - policyRule := policyDefinition.PolicyRule.(map[string]interface{}) - if policyRuleStr := flattenJSON(policyRule); policyRuleStr != "" { - d.Set("policy_rule", policyRuleStr) - roleIDs, _ := getPolicyRoleDefinitionIDs(policyRuleStr) - d.Set("role_definition_ids", roleIDs) - } else { - return fmt.Errorf("flattening Policy Definition Rule %q: %+v", name, err) - } + if metadataStr := flattenJSON(policyDefinition.Metadata); metadataStr != "" { + d.Set("metadata", metadataStr) + } - if metadataStr := flattenJSON(policyDefinition.Metadata); metadataStr != "" { - d.Set("metadata", metadataStr) - } + if parametersStr, err := flattenParameterDefinitionsValueToString(policyDefinition.Parameters); err == nil { + d.Set("parameters", parametersStr) + } else { + return fmt.Errorf("failed to flatten Policy Parameters %q: %+v", name, err) + } - if parametersStr, err := flattenParameterDefinitionsValueToString(policyDefinition.Parameters); err == nil { - d.Set("parameters", parametersStr) - } else { - return fmt.Errorf("failed to flatten Policy Parameters %q: %+v", name, err) + return nil } - - return nil } diff --git a/internal/services/policy/registration.go b/internal/services/policy/registration.go index f22756c2fae7..1a35bd55e437 100644 --- a/internal/services/policy/registration.go +++ b/internal/services/policy/registration.go @@ -47,6 +47,7 @@ func (r Registration) WebsiteCategories() []string { func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { return map[string]*pluginsdk.Resource{ "azurerm_policy_definition": dataSourceArmPolicyDefinition(), + "azurerm_policy_definition_built_in": dataSourceArmPolicyDefinitionBuiltIn(), "azurerm_policy_set_definition": dataSourceArmPolicySetDefinition(), "azurerm_policy_virtual_machine_configuration_assignment": dataSourcePolicyVirtualMachineConfigurationAssignment(), } diff --git a/website/docs/d/policy_definition_built_in.html.markdown b/website/docs/d/policy_definition_built_in.html.markdown new file mode 100644 index 000000000000..b7605346154a --- /dev/null +++ b/website/docs/d/policy_definition_built_in.html.markdown @@ -0,0 +1,59 @@ +--- +subcategory: "Policy" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_policy_definition_built_in" +description: |- + Get information about a Built-In Policy Definition. +--- + +# Data Source: azurerm_policy_definition_built_in + +Use this data source to access information about a Built-In Policy Definition. Retrieves Policy Definitions from your current subscription by default. + +## Example Usage + +```hcl +data "azurerm_policy_definition_built_in" "example" { + display_name = "Allowed resource types" +} + +output "id" { + value = data.azurerm_policy_definition_built_in.example.id +} +``` + +## Argument Reference + +* `name` - Specifies the name of the Policy Definition. Conflicts with `display_name`. + +* `display_name` - Specifies the display name of the Policy Definition. Conflicts with `name`. + +~> **NOTE** As `display_name` is not unique errors may occur when there are multiple policy definitions with same display name. + +* `management_group_name` - (Optional) Only retrieve Policy Definitions from this Management Group. + +## Attributes Reference + +* `id` - The ID of the Policy Definition. + +* `type` - The Type of Policy. + +* `description` - The Description of the Policy. + +* `policy_type` - The Type of the Policy. Possible values are `BuiltIn`, `Custom` and `NotSpecified`. + +* `policy_rule` - The Rule as defined (in JSON) in the Policy. + +* `role_definition_ids` - A list of role definition id extracted from `policy_rule` required for remediation. + +* `parameters` - Any Parameters defined in the Policy. + +* `metadata` - Any Metadata defined in the Policy. + +* `mode` - The Mode of the Policy. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `read` - (Defaults to 5 minutes) Used when retrieving the Policy Definition.