Skip to content

Commit

Permalink
Merge pull request #2549 from olohmann/azurerm_policy_assignment_iden…
Browse files Browse the repository at this point in the history
…tity_support

Support for Managed Identity on azurerm_policy_assignment
  • Loading branch information
tombuildsstuff authored Jan 3, 2019
2 parents 554f251 + ef87912 commit 3d63906
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 5 deletions.
4 changes: 4 additions & 0 deletions azurerm/common_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ func locationSchema() *schema.Schema {
return azure.SchemaLocation()
}

func locationSchemaOptional() *schema.Schema {
return azure.SchemaLocationOptional()
}

func locationForDataSourceSchema() *schema.Schema {
return azure.SchemaLocationForDataSource()
}
Expand Down
10 changes: 10 additions & 0 deletions azurerm/helpers/azure/location.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ func SchemaLocation() *schema.Schema {
}
}

func SchemaLocationOptional() *schema.Schema {
return &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
StateFunc: NormalizeLocation,
DiffSuppressFunc: SuppressLocationDiff,
}
}

func SchemaLocationForDataSource() *schema.Schema {
return &schema.Schema{
Type: schema.TypeString,
Expand Down
86 changes: 82 additions & 4 deletions azurerm/resource_arm_policy_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import (

func resourceArmPolicyAssignment() *schema.Resource {
return &schema.Resource{
Create: resourceArmPolicyAssignmentCreate,
Create: resourceArmPolicyAssignmentCreateOrUpdate,
Update: resourceArmPolicyAssignmentCreateOrUpdate,
Read: resourceArmPolicyAssignmentRead,
Delete: resourceArmPolicyAssignmentDelete,
Importer: &schema.ResourceImporter{
Expand Down Expand Up @@ -48,13 +49,41 @@ func resourceArmPolicyAssignment() *schema.Resource {
"description": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"display_name": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"location": locationSchemaOptional(),

"identity": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"type": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
string(policy.None),
string(policy.SystemAssigned),
}, false),
},
"principal_id": {
Type: schema.TypeString,
Computed: true,
},
"tenant_id": {
Type: schema.TypeString,
Computed: true,
},
},
},
},

"parameters": {
Expand All @@ -68,7 +97,7 @@ func resourceArmPolicyAssignment() *schema.Resource {
}
}

func resourceArmPolicyAssignmentCreate(d *schema.ResourceData, meta interface{}) error {
func resourceArmPolicyAssignmentCreateOrUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).policyAssignmentsClient
ctx := meta.(*ArmClient).StopContext

Expand All @@ -90,6 +119,15 @@ func resourceArmPolicyAssignmentCreate(d *schema.ResourceData, meta interface{})
assignment.AssignmentProperties.Description = utils.String(v)
}

if _, ok := d.GetOk("identity"); ok {
policyIdentity := expandAzureRmPolicyIdentity(d)
assignment.Identity = policyIdentity
}

if v := d.Get("location").(string); v != "" {
assignment.Location = utils.String(azureRMNormalizeLocation(v))
}

if v := d.Get("parameters").(string); v != "" {
expandedParams, err := structure.ExpandJsonFromString(v)
if err != nil {
Expand Down Expand Up @@ -146,6 +184,14 @@ func resourceArmPolicyAssignmentRead(d *schema.ResourceData, meta interface{}) e

d.Set("name", resp.Name)

if err := d.Set("identity", flattenAzureRmPolicyIdentity(resp.Identity)); err != nil {
return fmt.Errorf("Error setting `identity`: %+v", err)
}

if location := resp.Location; location != nil {
d.Set("location", azureRMNormalizeLocation(*location))
}

if props := resp.AssignmentProperties; props != nil {
d.Set("scope", props.Scope)
d.Set("policy_definition_id", props.PolicyDefinitionID)
Expand Down Expand Up @@ -194,3 +240,35 @@ func policyAssignmentRefreshFunc(ctx context.Context, client policy.AssignmentsC
return res, strconv.Itoa(res.StatusCode), nil
}
}

func expandAzureRmPolicyIdentity(d *schema.ResourceData) *policy.Identity {
v := d.Get("identity")
identities := v.([]interface{})
identity := identities[0].(map[string]interface{})

identityType := policy.ResourceIdentityType(identity["type"].(string))

policyIdentity := policy.Identity{
Type: identityType,
}

return &policyIdentity
}

func flattenAzureRmPolicyIdentity(identity *policy.Identity) []interface{} {
if identity == nil {
return make([]interface{}, 0)
}

result := make(map[string]interface{})
result["type"] = string(identity.Type)
if identity.PrincipalID != nil {
result["principal_id"] = *identity.PrincipalID
}

if identity.TenantID != nil {
result["tenant_id"] = *identity.TenantID
}

return []interface{}{result}
}
102 changes: 102 additions & 0 deletions azurerm/resource_arm_policy_assignment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,32 @@ func TestAccAzureRMPolicyAssignment_basic(t *testing.T) {
})
}

func TestAccAzureRMPolicyAssignment_deployIfNotExists_policy(t *testing.T) {
resourceName := "azurerm_policy_assignment.test"

ri := acctest.RandInt()
location := testLocation()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMPolicyAssignmentDestroy,
Steps: []resource.TestStep{
{
Config: testAzureRMPolicyAssignment_deployIfNotExists_policy(ri, location),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMPolicyAssignmentExists(resourceName),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAzureRMPolicyAssignment_complete(t *testing.T) {
resourceName := "azurerm_policy_assignment.test"

Expand Down Expand Up @@ -146,6 +172,82 @@ resource "azurerm_policy_assignment" "test" {
`, ri, ri, location, ri, location, ri)
}

func testAzureRMPolicyAssignment_deployIfNotExists_policy(ri int, location string) string {
return fmt.Sprintf(`
resource "azurerm_policy_definition" "test" {
name = "acctestpol-%d"
policy_type = "Custom"
mode = "All"
display_name = "acctestpol-%d"
policy_rule = <<POLICY_RULE
{
"if": {
"field": "type",
"equals": "Microsoft.Sql/servers/databases"
},
"then": {
"effect": "DeployIfNotExists",
"details": {
"type": "Microsoft.Sql/servers/databases/transparentDataEncryption",
"name": "current",
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"
],
"existenceCondition": {
"field": "Microsoft.Sql/transparentDataEncryption.status",
"equals": "Enabled"
},
"deployment": {
"properties": {
"mode": "incremental",
"template": {
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"fullDbName": {
"type": "string"
}
},
"resources": [{
"name": "[concat(parameters('fullDbName'), '/current')]",
"type": "Microsoft.Sql/servers/databases/transparentDataEncryption",
"apiVersion": "2014-04-01",
"properties": {
"status": "Enabled"
}
}]
},
"parameters": {
"fullDbName": {
"value": "[field('fullName')]"
}
}
}
}
}
}
}
POLICY_RULE
}
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}
resource "azurerm_policy_assignment" "test" {
name = "acctestpa-%d"
scope = "${azurerm_resource_group.test.id}"
policy_definition_id = "${azurerm_policy_definition.test.id}"
identity {
type = "SystemAssigned"
}
location = "%s"
}
`, ri, ri, ri, location, ri, location)
}

func testAzureRMPolicyAssignment_complete(ri int, location string) string {
return fmt.Sprintf(`
resource "azurerm_policy_definition" "test" {
Expand Down
29 changes: 28 additions & 1 deletion website/docs/r/policy_assignment.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ description: |-

# azurerm_policy_assignment

Configures the specified Policy Definition at the specified Scope.
Configures the specified Policy Definition at the specified Scope. Also, Policy Set Definitions are supported.

## Example Usage

Expand Down Expand Up @@ -79,6 +79,10 @@ The following arguments are supported:

* `policy_definition_id` - (Required) The ID of the Policy Definition to be applied at the specified Scope.

* `identity` - (Optional) An `identity` block.

* `location` - (Optional) The Azure location where this policy assignment should exist. This is required when an Identity is assigned. Changing this forces a new resource to be created.

* `description` - (Optional) A description to use for this Policy Assignment. Changing this forces a new resource to be created.

* `display_name` - (Optional) A friendly display name to use for this Policy Assignment. Changing this forces a new resource to be created.
Expand All @@ -87,12 +91,35 @@ The following arguments are supported:

~> **NOTE:** This value is required when the specified Policy Definition contains the `parameters` field.

---

An `identity` block supports the following:

* `type` - (Required) The Managed Service Identity Type of this Policy Assignment. Possible values are `SystemAssigned` (where Azure will generate a Service Principal for you), or `None` (no use of a Managed Service Identity).

~> **NOTE:** When `type` is set to `SystemAssigned`, identity the Principal ID can be retrieved after the policy has been assigned.

---


## Attributes Reference

The following attributes are exported:

* `id` - The Policy Assignment id.

* `identity` - An `identity` block.

---

An `identity` block exports the following:

* `principal_id` - The Principal ID of this Policy Assignment if `type` is `SystemAssigned`.

* `tenant_id` - The Tenant ID of this Policy Assignment if `type` is `SystemAssigned`.

---

## Import

Policy Assignments can be imported using the `policy name`, e.g.
Expand Down

0 comments on commit 3d63906

Please sign in to comment.