Repository: aws-cloudformation/rain
Pull request #98 (2023-01-26 13:23:38)

Add --role-arn option for remove sub command.
*Issue #, if available:* -

*Description of changes:* Add --role-arn option for remove sub command.

The PR https://github.com/aws-cloudformation/rain/pull/81 added this flag to the deploy command. On occasion we needed this flag for the remove (delete) command too.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.



Patch:
diff --git a/docs/rain_rm.md b/docs/rain_rm.md
index 44330572..14d84fdc 100644
--- a/docs/rain_rm.md
+++ b/docs/rain_rm.md
@@ -13,11 +13,12 @@ rain rm <stack>
 ### Options
 
 ```
-  -d, --detach           once removal has started, don't wait around for it to finish
-  -h, --help             help for rm
-  -p, --profile string   AWS profile name; read from the AWS CLI configuration file
-  -r, --region string    AWS region to use
-  -y, --yes              don't ask questions; just delete
+  -d, --detach            once removal has started, don't wait around for it to finish
+  -h, --help              help for rm
+  -p, --profile string    AWS profile name; read from the AWS CLI configuration file
+  -r, --region string     AWS region to use
+      --role-arn string   ARN of an IAM role that CloudFormation should assume to remove the stack
+  -y, --yes               don't ask questions; just delete
 ```
 
 ### Options inherited from parent commands
diff --git a/internal/aws/cfn/cfn.go b/internal/aws/cfn/cfn.go
index 2eb4117a..df89bbc0 100644
--- a/internal/aws/cfn/cfn.go
+++ b/internal/aws/cfn/cfn.go
@@ -133,11 +133,17 @@ func ListStacks() ([]types.StackSummary, error) {
 }
 
 // DeleteStack deletes a stack
-func DeleteStack(stackName string) error {
-	// Get the stack properties
-	_, err := getClient().DeleteStack(context.Background(), &cloudformation.DeleteStackInput{
+func DeleteStack(stackName string, roleArn string) error {
+	input := &cloudformation.DeleteStackInput{
 		StackName: &stackName,
-	})
+	}
+
+	// roleArn is optional
+	if roleArn != "" {
+		input.RoleARN = ptr.String(roleArn)
+	}
+
+	_, err := getClient().DeleteStack(context.Background(), input)
 
 	return err
 }
diff --git a/internal/aws/cfn/mock.go b/internal/aws/cfn/mock.go
index a9f38e35..b7f3e5b7 100644
--- a/internal/aws/cfn/mock.go
+++ b/internal/aws/cfn/mock.go
@@ -103,7 +103,7 @@ func ListStacks() ([]types.StackSummary, error) {
 }
 
 // DeleteStack deletes a stack
-func DeleteStack(stackName string) error {
+func DeleteStack(stackName string, roleArn string) error {
 	if s, ok := region().stacks[stackName]; ok {
 		s.stack.StackStatus = types.StackStatusDeleteComplete
 		return nil
diff --git a/internal/cmd/deploy/deploy.go b/internal/cmd/deploy/deploy.go
index 2db0621a..8c95d6e2 100644
--- a/internal/cmd/deploy/deploy.go
+++ b/internal/cmd/deploy/deploy.go
@@ -187,7 +187,7 @@ YAML:
 				}
 
 				if !stackExists {
-					err = cfn.DeleteStack(stackName)
+					err = cfn.DeleteStack(stackName, "")
 					if err != nil {
 						panic(ui.Errorf(err, "error deleting empty stack '%s'", stackName))
 					}
diff --git a/internal/cmd/deploy/util.go b/internal/cmd/deploy/util.go
index 3393c2ac..e23a5e30 100644
--- a/internal/cmd/deploy/util.go
+++ b/internal/cmd/deploy/util.go
@@ -234,7 +234,7 @@ func checkStack(stackName string) (types.Stack, bool) {
 			message := "Existing stack is empty; deleting it."
 			fmt.Println(message)
 
-			err := cfn.DeleteStack(stackName)
+			err := cfn.DeleteStack(stackName, "")
 			if err != nil {
 				panic(ui.Errorf(err, "unable to delete stack '%s'", stackName))
 			}
diff --git a/internal/cmd/rm/rm.go b/internal/cmd/rm/rm.go
index 84ef0d17..51f953e3 100644
--- a/internal/cmd/rm/rm.go
+++ b/internal/cmd/rm/rm.go
@@ -13,6 +13,7 @@ import (
 
 var yes bool
 var detach bool
+var roleArn string
 
 // Cmd is the rm command's entrypoint
 var Cmd = &cobra.Command{
@@ -61,7 +62,7 @@ var Cmd = &cobra.Command{
 
 		spinner.Pop()
 
-		err = cfn.DeleteStack(stackName)
+		err = cfn.DeleteStack(stackName, roleArn)
 		if err != nil {
 			panic(ui.Errorf(err, "unable to delete stack '%s'", stackName))
 		}
@@ -94,4 +95,5 @@ var Cmd = &cobra.Command{
 func init() {
 	Cmd.Flags().BoolVarP(&detach, "detach", "d", false, "once removal has started, don't wait around for it to finish")
 	Cmd.Flags().BoolVarP(&yes, "yes", "y", false, "don't ask questions; just delete")
+	Cmd.Flags().StringVar(&roleArn, "role-arn", "", "ARN of an IAM role that CloudFormation should assume to remove the stack")
 }


Test patch:
diff --git a/internal/cmd/rm/rm_test.go b/internal/cmd/rm/rm_test.go
index 3814accf..868eb61d 100644
--- a/internal/cmd/rm/rm_test.go
+++ b/internal/cmd/rm/rm_test.go
@@ -23,7 +23,8 @@ func Example_rm_help() {
 	//   rm, remove, del, delete
 	//
 	// Flags:
-	//   -d, --detach   once removal has started, don't wait around for it to finish
-	//   -h, --help     help for rm
-	//   -y, --yes      don't ask questions; just delete
+	//   -d, --detach            once removal has started, don't wait around for it to finish
+	//   -h, --help              help for rm
+	//       --role-arn string   ARN of an IAM role that CloudFormation should assume to remove the stack
+	//   -y, --yes               don't ask questions; just delete
 }