Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

assign policy to a managementgroup #2490

Merged
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
assign policy to a managementgroup
  • Loading branch information
lawrenae committed Dec 11, 2018
commit 41497321a0d215c85b5561c3646eb54d28e9ff94
74 changes: 61 additions & 13 deletions azurerm/resource_arm_policy_definition.go
Original file line number Diff line number Diff line change
@@ -4,13 +4,15 @@ import (
"context"
"fmt"
"log"
"regexp"
"strings"

"time"

"strconv"

"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2018-05-01/policy"
"github.com/Azure/go-autorest/autorest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/structure"
@@ -25,7 +27,7 @@ func resourceArmPolicyDefinition() *schema.Resource {
Read: resourceArmPolicyDefinitionRead,
Delete: resourceArmPolicyDefinitionDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
State: resourceArmPolicyDefinitionImport,
},

Schema: map[string]*schema.Schema{
@@ -56,6 +58,11 @@ func resourceArmPolicyDefinition() *schema.Resource {
}, true),
},

"management_group_id": {
Type: schema.TypeString,
Optional: true,
lawrenae marked this conversation as resolved.
Show resolved Hide resolved
},

"display_name": {
Type: schema.TypeString,
Required: true,
@@ -99,6 +106,7 @@ func resourceArmPolicyDefinitionCreateUpdate(d *schema.ResourceData, meta interf
mode := d.Get("mode").(string)
displayName := d.Get("display_name").(string)
description := d.Get("description").(string)
managementGroupID := d.Get("management_group_id").(string)

properties := policy.DefinitionProperties{
PolicyType: policy.Type(policyType),
@@ -136,7 +144,15 @@ func resourceArmPolicyDefinitionCreateUpdate(d *schema.ResourceData, meta interf
DefinitionProperties: &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 err
}

@@ -145,7 +161,7 @@ func resourceArmPolicyDefinitionCreateUpdate(d *schema.ResourceData, meta interf
stateConf := &resource.StateChangeConf{
Pending: []string{"404"},
Target: []string{"200"},
Refresh: policyDefinitionRefreshFunc(ctx, client, name),
Refresh: policyDefinitionRefreshFunc(ctx, client, name, managementGroupID),
Timeout: 5 * time.Minute,
MinTimeout: 10 * time.Second,
ContinuousTargetOccurence: 10,
@@ -154,7 +170,7 @@ func resourceArmPolicyDefinitionCreateUpdate(d *schema.ResourceData, meta interf
return fmt.Errorf("Error waiting for Policy Definition %q to become available: %s", name, err)
}

resp, err := client.Get(ctx, name)
resp, err := getPolicy(ctx, client, name, managementGroupID)
if err != nil {
return err
}
@@ -173,7 +189,10 @@ func resourceArmPolicyDefinitionRead(d *schema.ResourceData, meta interface{}) e
return err
}

resp, err := client.Get(ctx, name)
managementGroupID := d.Get("management_group_id").(string)

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

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

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

if props := resp.DefinitionProperties; props != nil {
d.Set("policy_type", props.PolicyType)
@@ -235,7 +255,14 @@ func resourceArmPolicyDefinitionDelete(d *schema.ResourceData, meta interface{})
return err
}

resp, err := client.Delete(ctx, name)
managementGroupID := d.Get("management_group_id").(string)
lawrenae marked this conversation as resolved.
Show resolved Hide resolved

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) {
@@ -255,20 +282,41 @@ func parsePolicyDefinitionNameFromId(id string) (string, error) {
return "", fmt.Errorf("Azure Policy Definition Id is empty or not formatted correctly: %s", id)
}

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

return components[6], nil
return components[len(components)-1], nil
}

func policyDefinitionRefreshFunc(ctx context.Context, client policy.DefinitionsClient, name string) resource.StateRefreshFunc {
func policyDefinitionRefreshFunc(ctx context.Context, client policy.DefinitionsClient, name string, managementGroupID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
res, err := client.Get(ctx, name)
res, err := getPolicy(ctx, client, name, managementGroupID)

if err != nil {
return nil, strconv.Itoa(res.StatusCode), fmt.Errorf("Error issuing read request in policyAssignmentRefreshFunc for Policy Assignment %q: %s", name, err)
}

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

func resourceArmPolicyDefinitionImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
r, _ := regexp.Compile("managementgroups/(.+)/providers/.*/policyDefinitions/(.+)$")
if r.MatchString(d.Id()) {
parms := r.FindAllStringSubmatch(d.Id(), -1)[0]
d.Set("management_group_id", parms[1])
d.Set("name", parms[2])

return []*schema.ResourceData{d}, nil
} else {
lawrenae marked this conversation as resolved.
Show resolved Hide resolved
//import a subscription based policy as before
return schema.ImportStatePassthrough(d, meta)
}
}

func getPolicy(ctx context.Context, client policy.DefinitionsClient, name string, managementGroupID string) (res policy.Definition, err error) {
lawrenae marked this conversation as resolved.
Show resolved Hide resolved
if managementGroupID == "" {
res, err = client.Get(ctx, name)
} else {
res, err = client.GetAtManagementGroup(ctx, name, managementGroupID)
}

return res, err
}
86 changes: 81 additions & 5 deletions azurerm/resource_arm_policy_definition_test.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ package azurerm
import (
"fmt"
"net/http"
"strings"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
@@ -21,7 +22,7 @@ func TestAccAzureRMPolicyDefinition_basic(t *testing.T) {
CheckDestroy: testCheckAzureRMPolicyDefinitionDestroy,
Steps: []resource.TestStep{
{
Config: testAzureRMPolicyDefinition_basic(ri),
Config: testAzureRMPolicyDefinition_basic(ri, false),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMPolicyDefinitionExists(resourceName),
),
@@ -35,6 +36,64 @@ func TestAccAzureRMPolicyDefinition_basic(t *testing.T) {
})
}

func TestAccAzureRMPolicyDefinitionAtMgmtGroup_basic(t *testing.T) {
resourceName := "azurerm_policy_definition.test"
mgmtGroupName := "azurerm_management_group.test"

ri := acctest.RandInt()

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

func testCheckAzureRMPolicyDefinitionExistsInMgmtGroup(policyName string, managementGroupName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[policyName]
if !ok {
return fmt.Errorf("not found: %s", policyName)
}

policyName := rs.Primary.Attributes["name"]

rs, ok = s.RootModule().Resources[managementGroupName]
if !ok {
return fmt.Errorf("not found: %s", managementGroupName)
}
lawrenae marked this conversation as resolved.
Show resolved Hide resolved

managementGroupID := rs.Primary.Attributes["group_id"]
lawrenae marked this conversation as resolved.
Show resolved Hide resolved

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

resp, err := client.GetAtManagementGroup(ctx, policyName, managementGroupID)
if err != nil {
return fmt.Errorf("Bad: GetAtManagementGroup on policyDefinitionsClient: %s", err)
}

if resp.StatusCode == http.StatusNotFound {
return fmt.Errorf("policy does not exist: %s", policyName)
}

return nil
}
}

func testCheckAzureRMPolicyDefinitionExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
@@ -85,14 +144,29 @@ func testCheckAzureRMPolicyDefinitionDestroy(s *terraform.State) error {
return nil
}

func testAzureRMPolicyDefinition_basic(ri int) string {
return fmt.Sprintf(`
func testAzureRMPolicyDefinition_basic(ri int, managementGroup bool) string {
lawrenae marked this conversation as resolved.
Show resolved Hide resolved
var builder strings.Builder

if managementGroup {
builder.WriteString(fmt.Sprintf(`
resource "azurerm_management_group" "test" {
display_name = "acctestmg-%d"
}
`, ri))
}

builder.WriteString(fmt.Sprintf(`
resource "azurerm_policy_definition" "test" {
name = "acctestpol-%d"
policy_type = "Custom"
mode = "All"
display_name = "acctestpol-%d"
display_name = "acctestpol-%d"`, ri, ri))

if managementGroup {
builder.WriteString(fmt.Sprintf(`management_group_id = "${azurerm_management_group.test.group_id}"`))
}

builder.WriteString(fmt.Sprintf(`
policy_rule = <<POLICY_RULE
{
"if": {
@@ -120,5 +194,7 @@ POLICY_RULE
}
PARAMETERS
}
`, ri, ri)
`))

return builder.String()
}
10 changes: 9 additions & 1 deletion website/docs/r/policy_definition.html.markdown
Original file line number Diff line number Diff line change
@@ -8,7 +8,9 @@ description: |-

# azurerm_policy_definition

Manages a policy rule definition. Policy definitions do not take effect until they are assigned to a scope using a Policy Assignment.
Manages a policy rule definition on a management group or your provider subscription.

Policy definitions do not take effect until they are assigned to a scope using a Policy Assignment.

## Example Usage

@@ -67,6 +69,8 @@ The following arguments are supported:

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

* `management_group_id` - (Optional) the management group id (IE: `${data.azurerm_management_group.test.group_id`) where where to define this policy
lawrenae marked this conversation as resolved.
Show resolved Hide resolved

* `policy_rule` - (Optional) The policy rule for the policy definition. This
is a json object representing the rule that contains an if and
a then block.
@@ -91,3 +95,7 @@ Policy Definitions can be imported using the `policy name`, e.g.
```shell
terraform import azurerm_policy_definition.testPolicy /subscriptions/<SUBSCRIPTION_ID>/providers/Microsoft.Authorization/policyDefinitions/<POLICY_NAME>
```
or
```shell
terraform import azurerm_policy_definition.testPolicy /providers/Microsoft.Management/managementgroups/<MANGAGEMENT_GROUP_ID>/providers/Microsoft.Authorization/policyDefinitions/<POLICY_NAME>
```