diff --git a/CHANGELOG.md b/CHANGELOG.md index 687200914169..6a4e92709a4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 2.94.0 (Unreleased) +UPGRADE NOTES: + +* `azurerm_api_management_policy` - resources that were created with v2.92.0 will be marked as tainted due to a [bug](https://github.com/hashicorp/terraform-provider-azurerm/issues/15042). This version addresses the underlying issue, but the actual resource needs to either be untainted (via `terraform untaint`) or allow Terraform to delete the resource and create it again. + FEATURES: **New Data Source:** `azurerm_linux_function_app` [GH-15009] @@ -11,6 +15,7 @@ ENHANCEMENTS: BUG FIXES: +* `azurerm_api_management_policy` - fixing the Resource ID for `api_management_policy` when this was provisioned using version `2.92.0` of the Azure Provider [GH-15060] * `azurerm_bastion_host` - Fix crash by adding nil check for `copy_paste_enabled` [GH-15074] * `azurerm_dev_test_lab` - fix the unexpected diff on `key_vault_id` [GH-15054] * `azurerm_subscription_cost_management_export` - fix the update method by sending the ETag when updating a cost management export [GH-15017] diff --git a/internal/services/apimanagement/api_management_policy_resource.go b/internal/services/apimanagement/api_management_policy_resource.go index fd21bbe6555d..da39dea4d4a3 100644 --- a/internal/services/apimanagement/api_management_policy_resource.go +++ b/internal/services/apimanagement/api_management_policy_resource.go @@ -9,6 +9,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/apimanagement/mgmt/2021-08-01/apimanagement" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/migration" "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" @@ -24,6 +25,11 @@ func resourceApiManagementPolicy() *pluginsdk.Resource { // TODO: replace this with an importer which validates the ID during import Importer: pluginsdk.DefaultImporter(), + SchemaVersion: 1, + StateUpgraders: pluginsdk.StateUpgrades(map[int]pluginsdk.StateUpgrade{ + 0: migration.ApiManagementApiPolicyV0ToV1{}, + }), + Timeouts: &pluginsdk.ResourceTimeout{ Create: pluginsdk.DefaultTimeout(30 * time.Minute), Read: pluginsdk.DefaultTimeout(5 * time.Minute), @@ -64,12 +70,12 @@ func resourceApiManagementPolicyCreateUpdate(d *pluginsdk.ResourceData, meta int defer cancel() apiManagementID := d.Get("api_management_id").(string) - id, err := parse.ApiManagementID(apiManagementID) + apiMgmtId, err := parse.ApiManagementID(apiManagementID) if err != nil { return err } - resourceGroup := id.ResourceGroup - serviceName := id.ServiceName + resourceGroup := apiMgmtId.ResourceGroup + serviceName := apiMgmtId.ServiceName /* Other resources would have a check for d.IsNewResource() at this location, and would error out using `tf.ImportAsExistsError` if the resource already existed. @@ -105,10 +111,12 @@ func resourceApiManagementPolicyCreateUpdate(d *pluginsdk.ResourceData, meta int return fmt.Errorf("Either `xml_content` or `xml_link` must be set") } - if _, err := client.CreateOrUpdate(ctx, resourceGroup, serviceName, parameters, ""); err != nil { + _, err = client.CreateOrUpdate(ctx, resourceGroup, serviceName, parameters, "") + if err != nil { return fmt.Errorf("creating or updating Policy (Resource Group %q / API Management Service %q): %+v", resourceGroup, serviceName, err) } + id := parse.NewPolicyID(apiMgmtId.SubscriptionId, apiMgmtId.ResourceGroup, apiMgmtId.ServiceName, "policy") d.SetId(id.ID()) return resourceApiManagementPolicyRead(d, meta) diff --git a/internal/services/apimanagement/migration/policy.go b/internal/services/apimanagement/migration/policy.go new file mode 100644 index 000000000000..c52c6e5e4898 --- /dev/null +++ b/internal/services/apimanagement/migration/policy.go @@ -0,0 +1,61 @@ +package migration + +import ( + "context" + "fmt" + "html" + + "github.com/Azure/azure-sdk-for-go/services/apimanagement/mgmt/2021-08-01/apimanagement" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/apimanagement/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +var _ pluginsdk.StateUpgrade = ApiManagementApiPolicyV0ToV1{} + +type ApiManagementApiPolicyV0ToV1 struct{} + +func (ApiManagementApiPolicyV0ToV1) UpgradeFunc() pluginsdk.StateUpgraderFunc { + return func(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) { + apiMgmtId, err := parse.ApiManagementID(rawState["id"].(string)) + if err != nil { + return rawState, nil + } + id := parse.NewPolicyID(apiMgmtId.SubscriptionId, apiMgmtId.ResourceGroup, apiMgmtId.ServiceName, "policy") + rawState["id"] = id.ID() + + client := meta.(*clients.Client).ApiManagement.PolicyClient + resp, err := client.Get(ctx, id.ResourceGroup, id.ServiceName, apimanagement.PolicyExportFormatXML) + if err != nil { + return nil, fmt.Errorf("making Read request for API Management Policy (Resource Group %q / API Management Service %q / API %q): %+v", id.ResourceGroup, id.ServiceName, id.Name, err) + } + + if properties := resp.PolicyContractProperties; properties != nil { + // when you submit an `xml_link` to the API, the API downloads this link and stores it as `xml_content` + // as such there is no way to set `xml_link` and we'll let Terraform handle it + rawState["xml_content"] = html.UnescapeString(*properties.Value) + } + return rawState, nil + } +} + +func (ApiManagementApiPolicyV0ToV1) Schema() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "api_management_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + }, + + "xml_content": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + }, + + "xml_link": { + Type: pluginsdk.TypeString, + Optional: true, + }, + } +}