-
Notifications
You must be signed in to change notification settings - Fork 4.6k
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
New Resource: azurerm_policy_definition
#1010
Changes from 4 commits
c287731
591d6ee
632dfbb
1c9c58b
ece0f49
163edd5
3046109
ee01577
74156c0
06a5842
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
package azurerm | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"net/url" | ||
"path" | ||
|
||
"github.com/Azure/azure-sdk-for-go/services/resources/mgmt/2016-12-01/policy" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
"github.com/hashicorp/terraform/helper/structure" | ||
"github.com/hashicorp/terraform/helper/validation" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" | ||
) | ||
|
||
func resourceArmPolicyDefinition() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceArmPolicyDefinitionCreateUpdate, | ||
Update: resourceArmPolicyDefinitionCreateUpdate, | ||
Read: resourceArmPolicyDefinitionRead, | ||
Delete: resourceArmPolicyDefinitionDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
|
||
"policy_type": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.StringInSlice([]string{ | ||
string(policy.TypeBuiltIn), | ||
string(policy.TypeCustom), | ||
string(policy.TypeNotSpecified), | ||
}, true)}, | ||
|
||
"mode": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.StringInSlice([]string{ | ||
string(policy.All), | ||
string(policy.Indexed), | ||
string(policy.NotSpecified), | ||
}, true), | ||
}, | ||
|
||
"display_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
|
||
"description": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
|
||
"policy_rule": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ValidateFunc: validation.ValidateJsonString, | ||
DiffSuppressFunc: structure.SuppressJsonDiff, | ||
}, | ||
|
||
"meta_data": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ValidateFunc: validation.ValidateJsonString, | ||
DiffSuppressFunc: structure.SuppressJsonDiff, | ||
}, | ||
|
||
"parameters": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ValidateFunc: validation.ValidateJsonString, | ||
DiffSuppressFunc: structure.SuppressJsonDiff, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceArmPolicyDefinitionCreateUpdate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*ArmClient).policyDefinitionsClient | ||
ctx := meta.(*ArmClient).StopContext | ||
|
||
name := d.Get("name").(string) | ||
policyType := d.Get("policy_type").(string) | ||
mode := d.Get("mode").(string) | ||
displayName := d.Get("display_name").(string) | ||
description := d.Get("description").(string) | ||
|
||
properties := policy.DefinitionProperties{ | ||
PolicyType: policy.Type(policyType), | ||
Mode: policy.Mode(mode), | ||
DisplayName: utils.String(displayName), | ||
Description: utils.String(description), | ||
} | ||
|
||
if policyRuleString := d.Get("policy_rule").(string); policyRuleString != "" { | ||
policyRule, err := structure.ExpandJsonFromString(policyRuleString) | ||
if err != nil { | ||
return fmt.Errorf("unable to parse policy_rule: %s", err) | ||
} | ||
properties.PolicyRule = &policyRule | ||
} | ||
|
||
if metaDataString := d.Get("meta_data").(string); metaDataString != "" { | ||
metaData, err := structure.ExpandJsonFromString(metaDataString) | ||
if err != nil { | ||
return fmt.Errorf("unable to parse meta_data: %s", err) | ||
} | ||
properties.Metadata = &metaData | ||
} | ||
|
||
if parametersString := d.Get("parameters").(string); parametersString != "" { | ||
parameters, err := structure.ExpandJsonFromString(parametersString) | ||
if err != nil { | ||
return fmt.Errorf("unable to parse parameters: %s", err) | ||
} | ||
properties.Parameters = ¶meters | ||
} | ||
|
||
definition := policy.Definition{ | ||
Name: utils.String(name), | ||
DefinitionProperties: &properties, | ||
} | ||
|
||
_, err := client.CreateOrUpdate(ctx, name, definition) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resp, err := client.Get(ctx, name) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.SetId(*resp.ID) | ||
|
||
return resourceArmPolicyDefinitionRead(d, meta) | ||
} | ||
|
||
func resourceArmPolicyDefinitionRead(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*ArmClient).policyDefinitionsClient | ||
ctx := meta.(*ArmClient).StopContext | ||
|
||
name, err := parsePolicyDefinitionNameFromId(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resp, err := client.Get(ctx, name) | ||
if err != nil { | ||
if utils.ResponseWasNotFound(resp.Response) { | ||
log.Printf("[INFO] Error reading Policy Definition %q - removing from state", d.Id()) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
return fmt.Errorf("Error reading Policy Definition %+v", err) | ||
} | ||
|
||
d.Set("id", resp.ID) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we don't need to set |
||
d.Set("name", resp.Name) | ||
if props := resp.DefinitionProperties; props != nil { | ||
d.Set("policy_type", props.PolicyType) | ||
d.Set("mode", props.Mode) | ||
d.Set("display_name", props.DisplayName) | ||
d.Set("description", props.Description) | ||
d.Set("policy_rule", props.PolicyRule) | ||
d.Set("meta_data", props.Metadata) | ||
d.Set("parameters", props.Parameters) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceArmPolicyDefinitionDelete(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*ArmClient).policyDefinitionsClient | ||
ctx := meta.(*ArmClient).StopContext | ||
|
||
name, err := parsePolicyDefinitionNameFromId(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resp, err := client.Delete(ctx, name) | ||
|
||
if err != nil { | ||
if utils.ResponseWasNotFound(resp) { | ||
return nil | ||
} | ||
|
||
return fmt.Errorf("Error deleting Policy Definition %q: %+v", name, err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func parsePolicyDefinitionNameFromId(id string) (string, error) { | ||
idURL, err := url.ParseRequestURI(id) | ||
if err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the ID should be in the format:
based on what I can see in the SDK - I think we should be able to use the built in functions here: We can access this via:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When we use parseAzureResourceID we get an error that a resource group isn't in the id string. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in which case can we return just the last segment of this? given we can guarantee there's only 6 segments in the URI (once it's split based on
|
||
return "", err | ||
} | ||
name := path.Base(idURL.Path) | ||
if name == "." || name == "/" { | ||
return "", fmt.Errorf("Couldn't parse Policy Definition name") | ||
} | ||
|
||
return name, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package azurerm | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
func TestAccAzureRMPolicyDefinition_basic(t *testing.T) { | ||
resourceName := "azurerm_policy_definition.test" | ||
|
||
ri := acctest.RandInt() | ||
config := testAzureRMPolicyDefinition(ri) | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testCheckAzureRMPolicyDefinitionDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: config, | ||
Check: resource.ComposeTestCheckFunc( | ||
testCheckAzureRMPolicyDefinitionExists(resourceName)), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testCheckAzureRMPolicyDefinitionExists(name string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources[name] | ||
if !ok { | ||
return fmt.Errorf("not found: %s", name) | ||
} | ||
|
||
policyName := rs.Primary.Attributes["name"] | ||
|
||
client := testAccProvider.Meta().(*ArmClient).policyDefinitionsClient | ||
ctx := testAccProvider.Meta().(*ArmClient).StopContext | ||
|
||
resp, err := client.Get(ctx, policyName) | ||
if err != nil { | ||
return fmt.Errorf("Bad: Get on policyDefinitionsClient: %s", err) | ||
} | ||
|
||
if resp.StatusCode == http.StatusNotFound { | ||
return fmt.Errorf("policy does not exist: %s", name) | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testCheckAzureRMPolicyDefinitionDestroy(s *terraform.State) error { | ||
client := testAccProvider.Meta().(*ArmClient).policyDefinitionsClient | ||
ctx := testAccProvider.Meta().(*ArmClient).StopContext | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "azurerm_policy_definition" { | ||
continue | ||
} | ||
|
||
name := rs.Primary.Attributes["name"] | ||
|
||
resp, err := client.Get(ctx, name) | ||
|
||
if err != nil { | ||
return nil | ||
} | ||
|
||
if resp.StatusCode != http.StatusNotFound { | ||
return fmt.Errorf("policy still exists:%s", *resp.Name) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func testAzureRMPolicyDefinition(ri int) 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": { | ||
"not": { | ||
"field": "location", | ||
"in": "[parameters('allowedLocations')]" | ||
} | ||
}, | ||
"then": { | ||
"effect": "audit" | ||
} | ||
} | ||
POLICY_RULE | ||
|
||
parameters =<<PARAMETERS | ||
{ | ||
"allowedLocations": { | ||
"type": "Array", | ||
"metadata": { | ||
"description": "The list of allowed locations for resources.", | ||
"displayName": "Allowed locations", | ||
"strongType": "location" | ||
} | ||
} | ||
} | ||
PARAMETERS | ||
}`, ri, ri) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add some documentation for this resource in the website folder?