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

Support for purge_soft_delete_on_destroy in azurerm_api_management #12850

Merged
merged 15 commits into from
Sep 16, 2021
3 changes: 3 additions & 0 deletions internal/features/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package features
func Default() UserFeatures {
return UserFeatures{
// NOTE: ensure all nested objects are fully populated
ApiManagement: ApiManagementFeatures{
PurgeSoftDeleteOnDestroy: false,
},
CognitiveAccount: CognitiveAccountFeatures{
PurgeSoftDeleteOnDestroy: true,
},
Expand Down
5 changes: 5 additions & 0 deletions internal/features/user_flags.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package features

type UserFeatures struct {
ApiManagement ApiManagementFeatures
CognitiveAccount CognitiveAccountFeatures
VirtualMachine VirtualMachineFeatures
VirtualMachineScaleSet VirtualMachineScaleSetFeatures
Expand Down Expand Up @@ -46,3 +47,7 @@ type LogAnalyticsWorkspaceFeatures struct {
type ResourceGroupFeatures struct {
PreventDeletionIfContainsResources bool
}

type ApiManagementFeatures struct {
PurgeSoftDeleteOnDestroy bool
}
24 changes: 24 additions & 0 deletions internal/provider/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ func schemaFeatures(supportLegacyTestSuite bool) *pluginsdk.Schema {
// specifying the block otherwise) - however for 2+ they should be optional
features := map[string]*pluginsdk.Schema{
// lintignore:XS003
"api_management": {
Type: pluginsdk.TypeList,
Optional: true,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"purge_soft_delete_on_destroy": {
Type: pluginsdk.TypeBool,
Optional: true,
},
},
},
},

"cognitive_account": {
Type: pluginsdk.TypeList,
Optional: true,
Expand Down Expand Up @@ -174,6 +188,16 @@ func expandFeatures(input []interface{}) features.UserFeatures {

val := input[0].(map[string]interface{})

if raw, ok := val["api_management"]; ok {
items := raw.([]interface{})
if len(items) > 0 && items[0] != nil {
apimRaw := items[0].(map[string]interface{})
if v, ok := apimRaw["purge_soft_delete_on_destroy"]; ok {
features.ApiManagement.PurgeSoftDeleteOnDestroy = v.(bool)
}
}
}

if raw, ok := val["cognitive_account"]; ok {
items := raw.([]interface{})
if len(items) > 0 && items[0] != nil {
Expand Down
84 changes: 84 additions & 0 deletions internal/provider/features_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ func TestExpandFeatures(t *testing.T) {
Name: "Empty Block",
Input: []interface{}{},
Expected: features.UserFeatures{
ApiManagement: features.ApiManagementFeatures{
PurgeSoftDeleteOnDestroy: false,
},
CognitiveAccount: features.CognitiveAccountFeatures{
PurgeSoftDeleteOnDestroy: true,
},
Expand Down Expand Up @@ -52,6 +55,11 @@ func TestExpandFeatures(t *testing.T) {
Name: "Complete Enabled",
Input: []interface{}{
map[string]interface{}{
"api_management": []interface{}{
map[string]interface{}{
"purge_soft_delete_on_destroy": true,
},
},
"cognitive_account": []interface{}{
map[string]interface{}{
"purge_soft_delete_on_destroy": true,
Expand Down Expand Up @@ -99,6 +107,9 @@ func TestExpandFeatures(t *testing.T) {
},
},
Expected: features.UserFeatures{
ApiManagement: features.ApiManagementFeatures{
PurgeSoftDeleteOnDestroy: true,
},
CognitiveAccount: features.CognitiveAccountFeatures{
PurgeSoftDeleteOnDestroy: true,
},
Expand Down Expand Up @@ -133,6 +144,11 @@ func TestExpandFeatures(t *testing.T) {
Name: "Complete Disabled",
Input: []interface{}{
map[string]interface{}{
"api_management": []interface{}{
map[string]interface{}{
"purge_soft_delete_on_destroy": false,
},
},
"cognitive_account": []interface{}{
map[string]interface{}{
"purge_soft_delete_on_destroy": false,
Expand Down Expand Up @@ -180,6 +196,9 @@ func TestExpandFeatures(t *testing.T) {
},
},
Expected: features.UserFeatures{
ApiManagement: features.ApiManagementFeatures{
PurgeSoftDeleteOnDestroy: false,
},
CognitiveAccount: features.CognitiveAccountFeatures{
PurgeSoftDeleteOnDestroy: false,
},
Expand Down Expand Up @@ -221,6 +240,71 @@ func TestExpandFeatures(t *testing.T) {
}
}

func TestExpandFeaturesApiManagement(t *testing.T) {
testData := []struct {
Name string
Input []interface{}
EnvVars map[string]interface{}
Expected features.UserFeatures
}{
{
Name: "Empty Block",
Input: []interface{}{
map[string]interface{}{
"api_management": []interface{}{},
},
},
Expected: features.UserFeatures{
ApiManagement: features.ApiManagementFeatures{
PurgeSoftDeleteOnDestroy: false,
},
},
},
{
Name: "Purge Soft Delete On Destroy Api Management Enabled",
Input: []interface{}{
map[string]interface{}{
"api_management": []interface{}{
map[string]interface{}{
"purge_soft_delete_on_destroy": true,
},
},
},
},
Expected: features.UserFeatures{
ApiManagement: features.ApiManagementFeatures{
PurgeSoftDeleteOnDestroy: true,
},
},
},
{
Name: "Purge Soft Delete On Destroy Api Management Disabled",
Input: []interface{}{
map[string]interface{}{
"api_management": []interface{}{
map[string]interface{}{
"purge_soft_delete_on_destroy": false,
},
},
},
},
Expected: features.UserFeatures{
ApiManagement: features.ApiManagementFeatures{
PurgeSoftDeleteOnDestroy: false,
},
},
},
}

for _, testCase := range testData {
t.Logf("[DEBUG] Test Case: %q", testCase.Name)
result := expandFeatures(testCase.Input)
if !reflect.DeepEqual(result.ApiManagement, testCase.Expected.ApiManagement) {
t.Fatalf("Expected %+v but got %+v", result.ApiManagement, testCase.Expected.ApiManagement)
}
}
}

func TestExpandFeaturesCognitiveServices(t *testing.T) {
testData := []struct {
Name string
Expand Down
22 changes: 22 additions & 0 deletions internal/services/apimanagement/api_management_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,28 @@ func resourceApiManagementServiceDelete(d *pluginsdk.ResourceData, meta interfac
}
}

// Purge the soft deleted Api Management permanently if the feature flag is enabled
if meta.(*clients.Client).Features.ApiManagement.PurgeSoftDeleteOnDestroy {
log.Printf("[DEBUG] Api Management %q marked for purge - executing purge", id)
deletedServicesClient := meta.(*clients.Client).ApiManagement.DeletedServicesClient
_, err := deletedServicesClient.GetByName(ctx, id.ServiceName, azure.NormalizeLocation(d.Get("location").(string)))
if err != nil {
return err
}
future, err := deletedServicesClient.Purge(ctx, id.ServiceName, azure.NormalizeLocation(d.Get("location").(string)))
if err != nil {
return err
}

log.Printf("[DEBUG] Waiting for purge of Api Management %q..", id)
err = future.WaitForCompletionRef(ctx, deletedServicesClient.Client)
if err != nil {
return fmt.Errorf("purging %s: %+v", *id, err)
}
log.Printf("[DEBUG] Purged Api Management %q.", id)
return nil
}

return nil
}

Expand Down
64 changes: 64 additions & 0 deletions internal/services/apimanagement/api_management_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,31 @@ func TestAccApiManagement_minApiVersion(t *testing.T) {
})
}

func TestAccApiManagement_purgeSoftDelete(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_api_management", "test")
r := ApiManagementResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.consumptionPurgeSoftDeleteRecovery(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.consumptionPurgeSoftDelete(data),
},
{
Config: r.consumptionPurgeSoftDeleteRecovery(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func (ApiManagementResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := parse.ApiManagementID(state.ID)
if err != nil {
Expand Down Expand Up @@ -1818,6 +1843,45 @@ resource "azurerm_api_management" "test" {
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger)
}

func (r ApiManagementResource) consumptionPurgeSoftDeleteRecovery(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}

resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}

resource "azurerm_api_management" "test" {
name = "acctestAM-%d"
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
publisher_name = "pub1"
publisher_email = "[email protected]"
sku_name = "Consumption_0"
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger)
}

func (ApiManagementResource) consumptionPurgeSoftDelete(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {
api_management {
purge_soft_delete_on_destroy = true
}
}
}

resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}
`, data.RandomInteger, data.Locations.Primary)
}

func (ApiManagementResource) tenantAccess(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
Expand Down
5 changes: 5 additions & 0 deletions internal/services/apimanagement/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Client struct {
CacheClient *apimanagement.CacheClient
CertificatesClient *apimanagement.CertificateClient
DiagnosticClient *apimanagement.DiagnosticClient
DeletedServicesClient *apimanagement.DeletedServicesClient
EmailTemplateClient *apimanagement.EmailTemplateClient
GatewayClient *apimanagement.GatewayClient
GatewayApisClient *apimanagement.GatewayAPIClient
Expand Down Expand Up @@ -83,6 +84,9 @@ func NewClient(o *common.ClientOptions) *Client {
diagnosticClient := apimanagement.NewDiagnosticClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&diagnosticClient.Client, o.ResourceManagerAuthorizer)

deletedServicesClient := apimanagement.NewDeletedServicesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&deletedServicesClient.Client, o.ResourceManagerAuthorizer)

emailTemplateClient := apimanagement.NewEmailTemplateClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&emailTemplateClient.Client, o.ResourceManagerAuthorizer)

Expand Down Expand Up @@ -163,6 +167,7 @@ func NewClient(o *common.ClientOptions) *Client {
CacheClient: &cacheClient,
CertificatesClient: &certificatesClient,
DiagnosticClient: &diagnosticClient,
DeletedServicesClient: &deletedServicesClient,
EmailTemplateClient: &emailTemplateClient,
GatewayClient: &gatewayClient,
GatewayApisClient: &gatewayApisClient,
Expand Down
8 changes: 8 additions & 0 deletions website/docs/index.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ It's possible to configure the behaviour of certain resources using the `feature

The `features` block supports the following:

* `api_management` - (Optional) An `api_management` block as defined below.

* `cognitive_account` - (Optional) A `cognitive_account` block as defined below.

* `key_vault` - (Optional) A `key_vault` block as defined below.
Expand All @@ -185,6 +187,12 @@ The `features` block supports the following:

---

The `api_management` block supports the following:

* `purge_soft_delete_on_destroy` - (Optional) Should the `azurerm_api_management` resources be permanently deleted (e.g. purged) when destroyed? Defaults to `false`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also place a note in azurerm_api_management resource referring to this feature flag.


---

The `cognitive_account` block supports the following:

* `purge_soft_delete_on_destroy` - (Optional) Should the `azurerm_cognitive_account` resources be permanently deleted (e.g. purged) when destroyed? Defaults to `true`.
Expand Down