Skip to content

Commit

Permalink
Merge pull request #2618 from olohmann/azurerm_policy_set_definition_…
Browse files Browse the repository at this point in the history
…mgmt_group_support

Support for Management Groups in azurerm_policy_set_definition
  • Loading branch information
katbyte committed Jan 9, 2019
2 parents 70fe9fd + 88ceb20 commit 657c776
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 22 deletions.
70 changes: 59 additions & 11 deletions azurerm/resource_arm_policy_set_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import (
"fmt"
"log"
"reflect"
"regexp"
"strconv"
"strings"
"time"

"github.com/Azure/go-autorest/autorest"

"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/policy"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
Expand Down Expand Up @@ -47,6 +50,12 @@ func resourceArmPolicySetDefinition() *schema.Resource {
}, false),
},

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

"display_name": {
Type: schema.TypeString,
Required: true,
Expand Down Expand Up @@ -106,6 +115,7 @@ func resourceArmPolicySetDefinitionCreateUpdate(d *schema.ResourceData, meta int
policyType := d.Get("policy_type").(string)
displayName := d.Get("display_name").(string)
description := d.Get("description").(string)
managementGroupID := d.Get("management_group_id").(string)

properties := policy.SetDefinitionProperties{
PolicyType: policy.Type(policyType),
Expand Down Expand Up @@ -143,7 +153,14 @@ func resourceArmPolicySetDefinitionCreateUpdate(d *schema.ResourceData, meta int
SetDefinitionProperties: &properties,
}

if _, err := client.CreateOrUpdate(ctx, name, definition); err != nil {
var err error
if managementGroupID == "" {
_, err = client.CreateOrUpdate(ctx, name, definition)
} else {
_, err = client.CreateOrUpdateAtManagementGroup(ctx, name, definition, managementGroupID)
}

if err != nil {
return fmt.Errorf("Error creating/updating Policy Set Definition %q: %s", name, err)
}

Expand All @@ -152,16 +169,18 @@ func resourceArmPolicySetDefinitionCreateUpdate(d *schema.ResourceData, meta int
stateConf := &resource.StateChangeConf{
Pending: []string{"404"},
Target: []string{"200"},
Refresh: policySetDefinitionRefreshFunc(ctx, client, name),
Refresh: policySetDefinitionRefreshFunc(ctx, client, name, managementGroupID),
Timeout: 5 * time.Minute,
MinTimeout: 10 * time.Second,
ContinuousTargetOccurence: 10,
}
if _, err := stateConf.WaitForState(); err != nil {

if _, err = stateConf.WaitForState(); err != nil {
return fmt.Errorf("Error waiting for Policy Set Definition %q to become available: %s", name, err)
}

resp, err := client.Get(ctx, name)
var resp policy.SetDefinition
resp, err = getPolicySetDefinition(ctx, client, name, managementGroupID)
if err != nil {
return fmt.Errorf("Error retrieving Policy Set Definition %q: %s", name, err)
}
Expand All @@ -180,7 +199,10 @@ func resourceArmPolicySetDefinitionRead(d *schema.ResourceData, meta interface{}
return err
}

resp, err := client.Get(ctx, name)
managementGroupID := parseManagementGroupIdFromPolicySetId(d.Id())

resp, err := getPolicySetDefinition(ctx, client, name, managementGroupID)

if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
log.Printf("[INFO] Error reading Policy Set Definition %q - removing from state", d.Id())
Expand All @@ -192,6 +214,7 @@ func resourceArmPolicySetDefinitionRead(d *schema.ResourceData, meta interface{}
}

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

if props := resp.SetDefinitionProperties; props != nil {
d.Set("policy_type", string(props.PolicyType))
Expand Down Expand Up @@ -241,7 +264,15 @@ func resourceArmPolicySetDefinitionDelete(d *schema.ResourceData, meta interface
return err
}

resp, err := client.Delete(ctx, name)
managementGroupID := parseManagementGroupIdFromPolicySetId(d.Id())

var resp autorest.Response
if managementGroupID == "" {
resp, err = client.Delete(ctx, name)
} else {
resp, err = client.DeleteAtManagementGroup(ctx, name, managementGroupID)
}

if err != nil {
if utils.ResponseWasNotFound(resp) {
return nil
Expand All @@ -260,20 +291,37 @@ func parsePolicySetDefinitionNameFromId(id string) (string, error) {
return "", fmt.Errorf("Azure Policy Set Definition Id is empty or not formatted correctly: %s", id)
}

if len(components) != 7 {
return "", fmt.Errorf("Azure Policy Set Definition Id should have 6 segments, got %d: '%s'", len(components)-1, id)
return components[len(components)-1], nil
}

func parseManagementGroupIdFromPolicySetId(id string) string {
r, _ := regexp.Compile("managementgroups/(.+)/providers/.*$")

if r.MatchString(id) {
matches := r.FindAllStringSubmatch(id, -1)[0]
return matches[1]
}

return components[6], nil
return ""
}

func policySetDefinitionRefreshFunc(ctx context.Context, client policy.SetDefinitionsClient, name string) resource.StateRefreshFunc {
func policySetDefinitionRefreshFunc(ctx context.Context, client policy.SetDefinitionsClient, name string, managementGroupId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
res, err := client.Get(ctx, name)
res, err := getPolicySetDefinition(ctx, client, name, managementGroupId)
if err != nil {
return nil, strconv.Itoa(res.StatusCode), fmt.Errorf("Error issuing read request in policySetDefinitionRefreshFunc for Policy Set Definition %q: %s", name, err)
}

return res, strconv.Itoa(res.StatusCode), nil
}
}

func getPolicySetDefinition(ctx context.Context, client policy.SetDefinitionsClient, name string, managementGroupID string) (res policy.SetDefinition, err error) {
if managementGroupID == "" {
res, err = client.Get(ctx, name)
} else {
res, err = client.GetAtManagementGroup(ctx, name, managementGroupID)
}

return res, err
}
108 changes: 97 additions & 11 deletions azurerm/resource_arm_policy_set_definition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package azurerm

import (
"fmt"
"net/http"
"testing"

"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/policy"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"

"github.com/hashicorp/terraform/terraform"

"github.com/hashicorp/terraform/helper/acctest"
Expand Down Expand Up @@ -61,6 +63,31 @@ func TestAccAzureRMPolicySetDefinition_custom(t *testing.T) {
})
}

func TestAccAzureRMPolicySetDefinition_ManagementGroup(t *testing.T) {
resourceName := "azurerm_policy_set_definition.test"

ri := acctest.RandInt()

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

func testAzureRMPolicySetDefinition_builtIn(ri int) string {
return fmt.Sprintf(`
resource "azurerm_policy_set_definition" "test" {
Expand Down Expand Up @@ -167,6 +194,47 @@ POLICY_DEFINITIONS
`, ri, ri, ri, ri)
}

func testAzureRMPolicySetDefinition_ManagementGroup(ri int) string {
return fmt.Sprintf(`
resource "azurerm_management_group" "test" {
display_name = "acctestmg-%d"
}
resource "azurerm_policy_set_definition" "test" {
name = "acctestpolset-%d"
policy_type = "Custom"
display_name = "acctestpolset-%d"
management_group_id = "${azurerm_management_group.test.group_id}"
parameters = <<PARAMETERS
{
"allowedLocations": {
"type": "Array",
"metadata": {
"description": "The list of allowed locations for resources.",
"displayName": "Allowed locations",
"strongType": "location"
}
}
}
PARAMETERS
policy_definitions = <<POLICY_DEFINITIONS
[
{
"parameters": {
"listOfAllowedLocations": {
"value": "[parameters('allowedLocations')]"
}
},
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e765b5de-1225-4ba3-bd56-1ac6695af988"
}
]
POLICY_DEFINITIONS
}
`, ri, ri, ri)
}

func testCheckAzureRMPolicySetDefinitionExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]
Expand All @@ -175,17 +243,25 @@ func testCheckAzureRMPolicySetDefinitionExists(resourceName string) resource.Tes
}

policySetName := rs.Primary.Attributes["name"]
managementGroupId := rs.Primary.Attributes["management_group_id"]

client := testAccProvider.Meta().(*ArmClient).policySetDefinitionsClient
ctx := testAccProvider.Meta().(*ArmClient).StopContext

resp, err := client.Get(ctx, policySetName)
if err != nil {
return fmt.Errorf("Bad: Get on policySetDefinitionsClient: %s", err)
var err error
var resp policy.SetDefinition
if managementGroupId != "" {
resp, err = client.GetAtManagementGroup(ctx, policySetName, managementGroupId)
} else {
resp, err = client.Get(ctx, policySetName)
}

if resp.StatusCode == http.StatusNotFound {
return fmt.Errorf("policy set definition does not exist: %s", policySetName)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
return fmt.Errorf("policy set definition does not exist: %s", policySetName)
} else {
return fmt.Errorf("Bad: Get on policySetDefinitionsClient: %s", err)
}
}

return nil
Expand All @@ -201,16 +277,26 @@ func testCheckAzureRMPolicySetDefinitionDestroy(s *terraform.State) error {
continue
}

name := rs.Primary.Attributes["name"]
policySetName := rs.Primary.Attributes["name"]
managementGroupId := rs.Primary.Attributes["management_group_id"]

resp, err := client.Get(ctx, name)
if err != nil {
return nil
var err error
var resp policy.SetDefinition
if managementGroupId != "" {
resp, err = client.GetAtManagementGroup(ctx, policySetName, managementGroupId)
} else {
resp, err = client.Get(ctx, policySetName)
}

if resp.StatusCode != http.StatusNotFound {
if err == nil {
return fmt.Errorf("policy set definition still exists: %s", *resp.Name)
}

if utils.ResponseWasNotFound(resp.Response) {
return nil
} else {
return err
}
}

return nil
Expand Down
8 changes: 8 additions & 0 deletions website/docs/r/policy_set_definition.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ The following arguments are supported:

* `description` - (Optional) The description of the policy set definition.

* `management_group_id` - (Optional) The ID of the Management Group where this policy should be defined. Changing this forces a new resource to be created.

~> **Note:** if you are using `azurerm_management_group` to assign a value to `management_group_id`, be sure to use `.group_id` and not `.id`.

* `metadata` - (Optional) The metadata for the policy set definition. This is a json object representing additional metadata that should be stored with the policy definition.

* `parameters` - (Optional) Parameters for the policy set definition. This field is a json object that allows you to parameterize your policy definition.
Expand All @@ -79,3 +83,7 @@ Policy Set Definitions can be imported using the Resource ID, e.g.
```shell
terraform import azurerm_policy_set_definition.test /subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Authorization/policySetDefinitions/testPolicySet
```
or
```shell
terraform import azurerm_policy_set_definition.test /providers/Microsoft.Management/managementgroups/my-mgmt-group-id/providers/Microsoft.Authorization/policySetDefinitions/testPolicySet
```

0 comments on commit 657c776

Please sign in to comment.