Repository: gruntwork-io/cloud-nuke
Pull request #590 (2023-09-20 02:56:04)

Remove RemediationConfiguration before deleting config rules
<!-- Prepend '[WIP]' to the title if this PR is still a work-in-progress. Remove it when it is ready for review! -->

## Description

Partially address https://github.com/gruntwork-io/cloud-nuke/issues/410. 

<!-- Description of the changes introduced by this PR. -->

## TODOs

Read the [Gruntwork contribution guidelines](https://gruntwork.notion.site/Gruntwork-Coding-Methodology-02fdcd6e4b004e818553684760bf691e).

- [ ] Update the docs.
- [ ] Run the relevant tests successfully, including pre-commit checks.
- [ ] Ensure any 3rd party code adheres with our [license policy](https://www.notion.so/gruntwork/Gruntwork-licenses-and-open-source-usage-policy-f7dece1f780341c7b69c1763f22b1378) or delete this line if its not applicable.
- [ ] Include release notes. If this PR is backward incompatible, include a migration guide.
- [ ] Attention Grunts - if this PR adds support for a new resource, ensure the `nuke_sandbox` and `nuke_phxdevops` jobs in `.circleci/config.yml` have been updated with appropriate exclusions (either directly in the job or via the `.circleci/nuke_config.yml` file) to prevent nuking IAM roles, groups, resources, etc that are important for the test accounts.


## Release Notes (draft)

<!-- One-line description of the PR that can be included in the final release notes. -->
Added / Removed / Updated [X].

### Migration Guide

<!-- Important: If you made any backward incompatible changes, then you must write a migration guide! -->



hongil0316: Hey @arsci, had to address tiny error. Can I get another approval on it? 

Patch:
diff --git a/aws/resources/config_service.go b/aws/resources/config_service.go
index 0454afe..30f06eb 100644
--- a/aws/resources/config_service.go
+++ b/aws/resources/config_service.go
@@ -2,14 +2,14 @@ package resources
 
 import (
 	"context"
+	"fmt"
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/service/configservice"
 	"github.com/gruntwork-io/cloud-nuke/config"
 	"github.com/gruntwork-io/cloud-nuke/logging"
 	"github.com/gruntwork-io/cloud-nuke/report"
-	"github.com/gruntwork-io/cloud-nuke/telemetry"
 	"github.com/gruntwork-io/go-commons/errors"
-	commonTelemetry "github.com/gruntwork-io/go-commons/telemetry"
+	"github.com/pterm/pterm"
 )
 
 func (csr *ConfigServiceRule) getAll(c context.Context, configObj config.Config) ([]*string, error) {
@@ -19,7 +19,7 @@ func (csr *ConfigServiceRule) getAll(c context.Context, configObj config.Config)
 		for _, configRule := range output.ConfigRules {
 			if configObj.ConfigServiceRule.ShouldInclude(config.ResourceValue{
 				Name: configRule.ConfigRuleName,
-			}) {
+			}) && *configRule.ConfigRuleState == "ACTIVE" {
 				configRuleNames = append(configRuleNames, configRule.ConfigRuleName)
 			}
 		}
@@ -46,33 +46,43 @@ func (csr *ConfigServiceRule) nukeAll(configRuleNames []string) error {
 	var deletedConfigRuleNames []*string
 
 	for _, configRuleName := range configRuleNames {
-		params := &configservice.DeleteConfigRuleInput{
+		pterm.Debug.Println(fmt.Sprintf("Start deleting config service rule: %s", configRuleName))
+		_, err := csr.Client.DeleteRemediationConfiguration(&configservice.DeleteRemediationConfigurationInput{
 			ConfigRuleName: aws.String(configRuleName),
-		}
-		_, err := csr.Client.DeleteConfigRule(params)
+		})
+		if err != nil {
+			pterm.Error.Println(fmt.Sprintf("Failed to delete remediation configuration w/ err %s", err))
+			report.Record(report.Entry{
+				Identifier:   configRuleName,
+				ResourceType: "Config service rule",
+				Error:        err,
+			})
 
-		// Record status of this resource
-		e := report.Entry{
-			Identifier:   configRuleName,
-			ResourceType: "Config service rule",
-			Error:        err,
+			continue
 		}
-		report.Record(e)
 
+		params := &configservice.DeleteConfigRuleInput{
+			ConfigRuleName: aws.String(configRuleName),
+		}
+		_, err = csr.Client.DeleteConfigRule(params)
 		if err != nil {
-			logging.Logger.Debugf("[Failed] %s", err)
-			telemetry.TrackEvent(commonTelemetry.EventContext{
-				EventName: "Error Nuking Config Service Rule",
-			}, map[string]interface{}{
-				"region": csr.Region,
+			pterm.Error.Println(fmt.Sprintf("Failed to delete config rule w/ err %s", err))
+			report.Record(report.Entry{
+				Identifier:   configRuleName,
+				ResourceType: "Config service rule",
+				Error:        err,
 			})
-		} else {
-			deletedConfigRuleNames = append(deletedConfigRuleNames, aws.String(configRuleName))
-			logging.Logger.Debugf("Deleted Config service rule: %s", configRuleName)
 		}
-	}
 
-	logging.Logger.Debugf("[OK] %d Config service rules deleted in %s", len(deletedConfigRuleNames), csr.Region)
+		deletedConfigRuleNames = append(deletedConfigRuleNames, aws.String(configRuleName))
+		pterm.Debug.Println(fmt.Sprintf("Successfully deleted config service rule: %s", configRuleName))
+		report.Record(report.Entry{
+			Identifier:   configRuleName,
+			ResourceType: "Config service rule",
+		})
+	}
 
+	pterm.Debug.Println(
+		fmt.Sprintf("Completed deleting %d config service rules %s", len(deletedConfigRuleNames), csr.Region))
 	return nil
 }


Test patch:
diff --git a/aws/resources/config_service_test.go b/aws/resources/config_service_test.go
index 8d39ccf..4e1148f 100644
--- a/aws/resources/config_service_test.go
+++ b/aws/resources/config_service_test.go
@@ -14,8 +14,9 @@ import (
 
 type mockedConfigServiceRule struct {
 	configserviceiface.ConfigServiceAPI
-	DescribeConfigRulesOutput configservice.DescribeConfigRulesOutput
-	DeleteConfigRuleOutput    configservice.DeleteConfigRuleOutput
+	DescribeConfigRulesOutput            configservice.DescribeConfigRulesOutput
+	DeleteConfigRuleOutput               configservice.DeleteConfigRuleOutput
+	DeleteRemediationConfigurationOutput configservice.DeleteRemediationConfigurationOutput
 }
 
 func (m mockedConfigServiceRule) DescribeConfigRulesPages(input *configservice.DescribeConfigRulesInput, fn func(*configservice.DescribeConfigRulesOutput, bool) bool) error {
@@ -27,6 +28,10 @@ func (m mockedConfigServiceRule) DeleteConfigRule(input *configservice.DeleteCon
 	return &m.DeleteConfigRuleOutput, nil
 }
 
+func (m mockedConfigServiceRule) DeleteRemediationConfiguration(input *configservice.DeleteRemediationConfigurationInput) (*configservice.DeleteRemediationConfigurationOutput, error) {
+	return &m.DeleteRemediationConfigurationOutput, nil
+}
+
 func TestConfigServiceRule_GetAll(t *testing.T) {
 	telemetry.InitTelemetry("cloud-nuke", "")
 	t.Parallel()
@@ -37,8 +42,8 @@ func TestConfigServiceRule_GetAll(t *testing.T) {
 		Client: mockedConfigServiceRule{
 			DescribeConfigRulesOutput: configservice.DescribeConfigRulesOutput{
 				ConfigRules: []*configservice.ConfigRule{
-					{ConfigRuleName: aws.String(testName1)},
-					{ConfigRuleName: aws.String(testName2)},
+					{ConfigRuleName: aws.String(testName1), ConfigRuleState: aws.String("ACTIVE")},
+					{ConfigRuleName: aws.String(testName2), ConfigRuleState: aws.String("ACTIVE")},
 				},
 			},
 		},
@@ -80,7 +85,8 @@ func TestConfigServiceRule_NukeAll(t *testing.T) {
 
 	csr := ConfigServiceRule{
 		Client: mockedConfigServiceRule{
-			DeleteConfigRuleOutput: configservice.DeleteConfigRuleOutput{},
+			DeleteConfigRuleOutput:               configservice.DeleteConfigRuleOutput{},
+			DeleteRemediationConfigurationOutput: configservice.DeleteRemediationConfigurationOutput{},
 		},
 	}