diff --git a/azurerm/config.go b/azurerm/config.go
index 9d924c2725e0..fba59f340e1a 100644
--- a/azurerm/config.go
+++ b/azurerm/config.go
@@ -131,6 +131,7 @@ type ArmClient struct {
apiManagementGroupClient apimanagement.GroupClient
apiManagementGroupUsersClient apimanagement.GroupUserClient
apiManagementLoggerClient apimanagement.LoggerClient
+ apiManagementPolicyClient apimanagement.PolicyClient
apiManagementProductsClient apimanagement.ProductClient
apiManagementProductApisClient apimanagement.ProductAPIClient
apiManagementProductGroupsClient apimanagement.ProductGroupClient
@@ -518,6 +519,10 @@ func (c *ArmClient) registerApiManagementServiceClients(endpoint, subscriptionId
c.configureClient(&serviceClient.Client, auth)
c.apiManagementServiceClient = serviceClient
+ policyClient := apimanagement.NewPolicyClientWithBaseURI(endpoint, subscriptionId)
+ c.configureClient(&policyClient.Client, auth)
+ c.apiManagementPolicyClient = policyClient
+
productsClient := apimanagement.NewProductClientWithBaseURI(endpoint, subscriptionId)
c.configureClient(&productsClient.Client, auth)
c.apiManagementProductsClient = productsClient
diff --git a/azurerm/helpers/suppress/xml.go b/azurerm/helpers/suppress/xml.go
new file mode 100644
index 000000000000..6f53ad2b769f
--- /dev/null
+++ b/azurerm/helpers/suppress/xml.go
@@ -0,0 +1,47 @@
+package suppress
+
+import (
+ "encoding/xml"
+ "io"
+ "reflect"
+ "strings"
+
+ "github.com/hashicorp/terraform/helper/schema"
+)
+
+func SuppressXmlDiff(_, old, new string, _ *schema.ResourceData) bool {
+ oldTokens, err := expandXmlTokensFromString(old)
+ if err != nil {
+ return false
+ }
+
+ newTokens, err := expandXmlTokensFromString(new)
+ if err != nil {
+ return false
+ }
+
+ return reflect.DeepEqual(oldTokens, newTokens)
+}
+
+// This function will extract all XML tokens from a string, but ignoring all white-space tokens
+func expandXmlTokensFromString(input string) ([]xml.Token, error) {
+ decoder := xml.NewDecoder(strings.NewReader(input))
+ tokens := make([]xml.Token, 0)
+ for {
+ token, err := decoder.Token()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return nil, err
+ }
+ if chars, ok := token.(xml.CharData); ok {
+ text := string(chars)
+ if strings.TrimSpace(text) == "" {
+ continue
+ }
+ }
+ tokens = append(tokens, xml.CopyToken(token))
+ }
+ return tokens, nil
+}
diff --git a/azurerm/helpers/suppress/xml_test.go b/azurerm/helpers/suppress/xml_test.go
new file mode 100644
index 000000000000..3a10a284238e
--- /dev/null
+++ b/azurerm/helpers/suppress/xml_test.go
@@ -0,0 +1,81 @@
+package suppress
+
+import "testing"
+
+func TestSuppressXmlDiff(t *testing.T) {
+ cases := []struct {
+ Name string
+ XmlA string
+ XmlB string
+ Suppress bool
+ }{
+ {
+ Name: "empty",
+ XmlA: "",
+ XmlB: "",
+ Suppress: true,
+ },
+ {
+ Name: "neither are xml",
+ XmlA: "this is not an xml",
+ XmlB: "neither is this",
+ Suppress: false,
+ },
+ {
+ Name: "identical texts",
+ XmlA: "this is not an xml",
+ XmlB: "this is not an xml",
+ Suppress: true,
+ },
+ {
+ Name: "xml vs text",
+ XmlA: "",
+ XmlB: "this is not an xml",
+ Suppress: false,
+ },
+ {
+ Name: "text vs xml",
+ XmlA: "this is not an xml",
+ XmlB: "",
+ Suppress: false,
+ },
+ {
+ Name: "identical xml",
+ XmlA: "",
+ XmlB: "",
+ Suppress: true,
+ },
+ {
+ Name: "xml with different line endings",
+ XmlA: "\n\n\n",
+ XmlB: "\r\n\r\n\r\n",
+ Suppress: true,
+ },
+ {
+ Name: "xml with different indentations",
+ XmlA: "\n \n \n",
+ XmlB: "\r\n\t\r\n\t\r\n",
+ Suppress: true,
+ },
+ {
+ Name: "xml with different quotation marks",
+ XmlA: "",
+ XmlB: "\r\n\t\r\n\t\r\n",
+ Suppress: true,
+ },
+ {
+ Name: "xml with different spaces",
+ XmlA: "",
+ XmlB: "\r\n\t\r\n\t\r\n",
+ Suppress: true,
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.Name, func(t *testing.T) {
+ if SuppressXmlDiff("test", tc.XmlA, tc.XmlB, nil) != tc.Suppress {
+ t.Fatalf("Expected SuppressXmlDiff to return %t for '%q' == '%q'", tc.Suppress, tc.XmlA, tc.XmlB)
+ }
+ })
+ }
+}
diff --git a/azurerm/provider.go b/azurerm/provider.go
index 5ebae851ffe8..e146bcb361aa 100644
--- a/azurerm/provider.go
+++ b/azurerm/provider.go
@@ -173,6 +173,7 @@ func Provider() terraform.ResourceProvider {
"azurerm_api_management_group": resourceArmApiManagementGroup(),
"azurerm_api_management_group_user": resourceArmApiManagementGroupUser(),
"azurerm_api_management_logger": resourceArmApiManagementLogger(),
+ "azurerm_api_management_policy": resourceArmApiManagementPolicy(),
"azurerm_api_management_product": resourceArmApiManagementProduct(),
"azurerm_api_management_product_api": resourceArmApiManagementProductApi(),
"azurerm_api_management_product_group": resourceArmApiManagementProductGroup(),
diff --git a/azurerm/resource_arm_api_management_policy.go b/azurerm/resource_arm_api_management_policy.go
new file mode 100644
index 000000000000..d393be9321f6
--- /dev/null
+++ b/azurerm/resource_arm_api_management_policy.go
@@ -0,0 +1,154 @@
+package azurerm
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/Azure/azure-sdk-for-go/services/apimanagement/mgmt/2018-01-01/apimanagement"
+ "github.com/hashicorp/terraform/helper/schema"
+ "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
+ "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress"
+ "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
+ "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
+)
+
+func resourceArmApiManagementPolicy() *schema.Resource {
+ return &schema.Resource{
+ Create: resourceArmApiManagementPolicyCreateUpdate,
+ Read: resourceArmApiManagementPolicyRead,
+ Update: resourceArmApiManagementPolicyCreateUpdate,
+ Delete: resourceArmApiManagementPolicyDelete,
+
+ Importer: &schema.ResourceImporter{
+ State: schema.ImportStatePassthrough,
+ },
+
+ Schema: map[string]*schema.Schema{
+ "resource_group_name": resourceGroupNameSchema(),
+
+ "api_management_name": azure.SchemaApiManagementName(),
+
+ "xml_content": {
+ Type: schema.TypeString,
+ Optional: true,
+ DiffSuppressFunc: suppress.SuppressXmlDiff,
+ ConflictsWith: []string{"xml_link"},
+ },
+
+ "xml_link": {
+ Type: schema.TypeString,
+ Optional: true,
+ ConflictsWith: []string{"xml_content"},
+ },
+ },
+ }
+}
+
+func resourceArmApiManagementPolicyCreateUpdate(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*ArmClient).apiManagementPolicyClient
+ ctx := meta.(*ArmClient).StopContext
+
+ resourceGroup := d.Get("resource_group_name").(string)
+ serviceName := d.Get("api_management_name").(string)
+
+ if requireResourcesToBeImported {
+ resp, err := client.Get(ctx, resourceGroup, serviceName)
+ if err != nil {
+ if !utils.ResponseWasNotFound(resp.Response) {
+ return fmt.Errorf("Error checking for present of existing Global Policy (API Management Service %q / Resource Group %q): %+v", serviceName, resourceGroup, err)
+ }
+ }
+ if !utils.ResponseWasNotFound(resp.Response) {
+ return tf.ImportAsExistsError("azurerm_api_management_policy", *resp.ID)
+ }
+ }
+
+ xmlContent := d.Get("xml_content").(string)
+ xmlLink := d.Get("xml_link").(string)
+ if xmlContent == "" && xmlLink == "" {
+ return fmt.Errorf("Either `xml_content` or `xml_link` is required")
+ }
+
+ content := xmlContent
+ format := apimanagement.XML
+ if xmlLink != "" {
+ content = xmlLink
+ format = apimanagement.XMLLink
+ }
+
+ parameters := apimanagement.PolicyContract{
+ PolicyContractProperties: &apimanagement.PolicyContractProperties{
+ ContentFormat: format,
+ PolicyContent: utils.String(content),
+ },
+ }
+
+ if _, err := client.CreateOrUpdate(ctx, resourceGroup, serviceName, parameters); err != nil {
+ return fmt.Errorf("Error creating Global Policy (API Management Service %q / Resource Group %q): %+v", serviceName, resourceGroup, err)
+ }
+
+ resp, err := client.Get(ctx, resourceGroup, serviceName)
+ if err != nil {
+ return fmt.Errorf("Error retrieving Global Policy (API Management Service %q / Resource Group %q): %+v", serviceName, resourceGroup, err)
+ }
+ if resp.ID == nil {
+ return fmt.Errorf("Cannot read Global Policy (API Management Service %q / Resource Group %q) ID", serviceName, resourceGroup)
+ }
+ d.SetId(*resp.ID)
+
+ return resourceArmApiManagementPolicyRead(d, meta)
+}
+
+func resourceArmApiManagementPolicyRead(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*ArmClient).apiManagementPolicyClient
+ ctx := meta.(*ArmClient).StopContext
+
+ id, err := parseAzureResourceID(d.Id())
+ if err != nil {
+ return err
+ }
+ resourceGroup := id.ResourceGroup
+ serviceName := id.Path["service"]
+
+ resp, err := client.Get(ctx, resourceGroup, serviceName)
+ if err != nil {
+ if utils.ResponseWasNotFound(resp.Response) {
+ log.Printf("[INFO] Api Management Policy %q does not exist - removing from state", d.Id())
+ d.SetId("")
+ return nil
+ }
+ return fmt.Errorf("Error reading Global Policy (API Management Service %q / Resource Group %q): %+v", serviceName, resourceGroup, err)
+ }
+
+ d.Set("resource_group_name", resourceGroup)
+ d.Set("api_management_name", serviceName)
+ if properties := resp.PolicyContractProperties; properties != nil {
+ d.Set("xml_content", "")
+ d.Set("xml_link", "")
+ if properties.ContentFormat == apimanagement.XML {
+ d.Set("xml_content", properties.PolicyContent)
+ } else if properties.ContentFormat == apimanagement.XMLLink {
+ d.Set("xml_link", properties.PolicyContent)
+ }
+ }
+
+ return nil
+}
+
+func resourceArmApiManagementPolicyDelete(d *schema.ResourceData, meta interface{}) error {
+ client := meta.(*ArmClient).apiManagementPolicyClient
+ ctx := meta.(*ArmClient).StopContext
+
+ id, err := parseAzureResourceID(d.Id())
+ if err != nil {
+ return err
+ }
+ resourceGroup := id.ResourceGroup
+ serviceName := id.Path["service"]
+
+ if _, err := client.Delete(ctx, resourceGroup, serviceName, ""); err != nil {
+ return fmt.Errorf("Error deleting Global Policy (API Management Service %q / Resource Group %q): %+v", serviceName, resourceGroup, err)
+ }
+
+ return nil
+}
diff --git a/azurerm/resource_arm_api_management_policy_test.go b/azurerm/resource_arm_api_management_policy_test.go
new file mode 100644
index 000000000000..e4beb92f0ea7
--- /dev/null
+++ b/azurerm/resource_arm_api_management_policy_test.go
@@ -0,0 +1,202 @@
+package azurerm
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform/helper/resource"
+ "github.com/hashicorp/terraform/terraform"
+ "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
+ "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
+)
+
+func TestAccAzureRMApiManagementPolicy_basic(t *testing.T) {
+ resourceName := "azurerm_api_management_policy.test"
+ ri := tf.AccRandTimeInt()
+ location := testLocation()
+
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testCheckAzureRMApiManagementPolicyDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccAzureRMApiManagementPolicy_basic(ri, location),
+ Check: resource.ComposeTestCheckFunc(
+ testCheckAzureRMApiManagementPolicyExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, "xml_content", ""),
+ ),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAccAzureRMApiManagementPolicy_findReplace(t *testing.T) {
+ resourceName := "azurerm_api_management_policy.test"
+ ri := tf.AccRandTimeInt()
+ location := testLocation()
+
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testCheckAzureRMApiManagementPolicyDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccAzureRMApiManagementPolicy_findReplace(ri, location),
+ Check: resource.ComposeTestCheckFunc(
+ testCheckAzureRMApiManagementPolicyExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, "xml_content", "\r\n\t\r\n\t\t\r\n\t\r\n"),
+ ),
+ },
+ {
+ ResourceName: resourceName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAccAzureRMApiManagementPolicy_update(t *testing.T) {
+ resourceName := "azurerm_api_management_policy.test"
+ ri := tf.AccRandTimeInt()
+ location := testLocation()
+
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testCheckAzureRMApiManagementPolicyDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccAzureRMApiManagementPolicy_basic(ri, location),
+ Check: resource.ComposeTestCheckFunc(
+ testCheckAzureRMApiManagementPolicyExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, "xml_content", ""),
+ ),
+ },
+ {
+ Config: testAccAzureRMApiManagementPolicy_findReplace(ri, location),
+ Check: resource.ComposeTestCheckFunc(
+ testCheckAzureRMApiManagementPolicyExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, "xml_content", "\r\n\t\r\n\t\t\r\n\t\r\n"),
+ ),
+ },
+ {
+ Config: testAccAzureRMApiManagementPolicy_basic(ri, location),
+ Check: resource.ComposeTestCheckFunc(
+ testCheckAzureRMApiManagementPolicyExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, "xml_content", ""),
+ ),
+ },
+ },
+ })
+}
+
+func testCheckAzureRMApiManagementPolicyExists(resourceName string) resource.TestCheckFunc {
+ return func(s *terraform.State) error {
+ rs, ok := s.RootModule().Resources[resourceName]
+ if !ok {
+ return fmt.Errorf("Api Management Policy not found: %s", resourceName)
+ }
+
+ resourceGroup := rs.Primary.Attributes["resource_group_name"]
+ serviceName := rs.Primary.Attributes["api_management_name"]
+
+ client := testAccProvider.Meta().(*ArmClient).apiManagementPolicyClient
+ ctx := testAccProvider.Meta().(*ArmClient).StopContext
+
+ if resp, err := client.Get(ctx, resourceGroup, serviceName); err != nil {
+ if utils.ResponseWasNotFound(resp.Response) {
+ return fmt.Errorf("Bad: Global Policy (API Management Service %q / Resource Group %q) does not exist", serviceName, resourceGroup)
+ }
+ return fmt.Errorf("Bad: Get on apiManagementPolicyClient: %+v", err)
+ }
+
+ return nil
+ }
+}
+
+func testCheckAzureRMApiManagementPolicyDestroy(s *terraform.State) error {
+ client := testAccProvider.Meta().(*ArmClient).apiManagementPolicyClient
+ ctx := testAccProvider.Meta().(*ArmClient).StopContext
+
+ for _, rs := range s.RootModule().Resources {
+ if rs.Type != "azurerm_api_management_policy" {
+ continue
+ }
+
+ resourceGroup := rs.Primary.Attributes["resource_group_name"]
+ serviceName := rs.Primary.Attributes["api_management_name"]
+
+ if resp, err := client.Get(ctx, resourceGroup, serviceName); err != nil {
+ if !utils.ResponseWasNotFound(resp.Response) {
+ return fmt.Errorf("Bad: Get on apiManagementPolicyClient: %+v", err)
+ }
+ }
+
+ return nil
+ }
+
+ return nil
+}
+
+func testAccAzureRMApiManagementPolicy_basic(rInt int, location string) string {
+ return fmt.Sprintf(`
+resource "azurerm_resource_group" "test" {
+ name = "acctestRG-%d"
+ location = "%s"
+}
+
+resource "azurerm_api_management" "test" {
+ name = "acctestAM-%d"
+ resource_group_name = "${azurerm_resource_group.test.name}"
+ location = "${azurerm_resource_group.test.location}"
+ publisher_name = "pub1"
+ publisher_email = "pub1@email.com"
+
+ sku = {
+ name = "Developer"
+ capacity = 1
+ }
+}
+
+resource "azurerm_api_management_policy" "test" {
+ resource_group_name = "${azurerm_resource_group.test.name}"
+ api_management_name = "${azurerm_api_management.test.name}"
+ xml_content = ""
+}
+`, rInt, location, rInt)
+}
+
+func testAccAzureRMApiManagementPolicy_findReplace(rInt int, location string) string {
+ return fmt.Sprintf(`
+resource "azurerm_resource_group" "test" {
+ name = "acctestRG-%d"
+ location = "%s"
+}
+
+resource "azurerm_api_management" "test" {
+ name = "acctestAM-%d"
+ resource_group_name = "${azurerm_resource_group.test.name}"
+ location = "${azurerm_resource_group.test.location}"
+ publisher_name = "pub1"
+ publisher_email = "pub1@email.com"
+
+ sku = {
+ name = "Developer"
+ capacity = 1
+ }
+}
+
+resource "azurerm_api_management_policy" "test" {
+ resource_group_name = "${azurerm_resource_group.test.name}"
+ api_management_name = "${azurerm_api_management.test.name}"
+ xml_content = ""
+}
+`, rInt, location, rInt)
+}
diff --git a/website/azurerm.erb b/website/azurerm.erb
index ca9a6b44c279..3962a487c896 100644
--- a/website/azurerm.erb
+++ b/website/azurerm.erb
@@ -354,6 +354,10 @@
azurerm_api_management_logger
+
>
+ azurerm_api_management_policy
+
+
>
azurerm_api_management_product
diff --git a/website/docs/r/api_management_policy.html.markdown b/website/docs/r/api_management_policy.html.markdown
new file mode 100644
index 000000000000..6790414dbd31
--- /dev/null
+++ b/website/docs/r/api_management_policy.html.markdown
@@ -0,0 +1,65 @@
+layout: "azurerm"
+page_title: "Azure Resource Manager: azurerm_api_management_policy"
+sidebar_current: "docs-azurerm-resource-api-management-policy"
+description: |-
+ Manages a global Policy within an API Management Service.
+---
+
+# azurerm_api_management_policy
+
+Manages a global Policy within an API Management Service.
+
+
+## Example Usage
+
+```hcl
+resource "azurerm_resource_group" "example" {
+ name = "example-rg"
+ location = "West US"
+}
+
+resource "azurerm_api_management" "example" {
+ name = "example-apim"
+ resource_group_name = "${azurerm_resource_group.example.name}"
+ location = "${azurerm_resource_group.example.location}"
+ publisher_name = "pub1"
+ publisher_email = "pub1@email.com"
+
+ sku = {
+ name = "Developer"
+ capacity = 1
+ }
+}
+
+resource "azurerm_api_management_policy" "example" {
+ resource_group_name = "${azurerm_resource_group.example.name}"
+ api_management_name = "${azurerm_api_management.example.name}"
+ xml_content = ""
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `resource_group_name` - (Required) The name of the Resource Group in which the API Management Service exists. Changing this forces a new resource to be created.
+
+* `api_management_name` - (Required) The name of the API Management Service where this Policy should be created. Changing this forces a new resource to be created.
+
+* `xml_content` - (Optional) The XML configuration of this Policy.
+
+* `xml_link` - (Optional) The HTTP endpoint of the XML configuration accessible from the API Management Service.
+
+## Attributes Reference
+
+The following attributes are exported:
+
+* `id` - The ID of the API Management Policy.
+
+
+## Import
+
+API Management Policy can be imported using the `resource id`, e.g.
+```shell
+$ terraform import azurerm_api_management_policy.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example-rg/Microsoft.ApiManagement/service/example-apim/policies/policy
+```