@@ -469,66 +469,91 @@ func SetMultitenancyEnvVars(prov client.ConfigProvider) error {
469
469
func deleteResourcesInCloudFormation (prov client.ConfigProvider , t * cfn_bootstrap.Template ) {
470
470
iamSvc := iam .New (prov )
471
471
temp := * renderCustomCloudFormation (t )
472
+ var (
473
+ iamRoles []* cfn_iam.Role
474
+ instanceProfiles []* cfn_iam.InstanceProfile
475
+ policies []* cfn_iam.ManagedPolicy
476
+ groups []* cfn_iam.Group
477
+ )
478
+ // the deletion order of these resources is important. Policies need to be last,
479
+ // so they don't have any attached resources which prevents their deletion.
480
+ // temp.Resources is a map. Traversing that directly results in undetermined order.
472
481
for _ , val := range temp .Resources {
473
- By (fmt .Sprintf ("deleting the following resource: %s" , val .AWSCloudFormationType ()))
474
- tayp := val .AWSCloudFormationType ()
475
- if tayp == configservice .ResourceTypeAwsIamRole {
482
+ switch val .AWSCloudFormationType () {
483
+ case configservice .ResourceTypeAwsIamRole :
476
484
role := val .(* cfn_iam.Role )
477
- By (fmt .Sprintf ("cleanup for role with name '%s'" , role .RoleName ))
478
- // added repeat to not keep flooding the logs.
479
- repeat := false
480
- Eventually (func (gomega Gomega ) bool {
481
- _ , err := iamSvc .DeleteRole (& iam.DeleteRoleInput {RoleName : aws .String (role .RoleName )})
482
- if err != nil && ! repeat {
483
- By (fmt .Sprintf ("failed to delete role '%s'; reason: %+v" , role .RoleName , err ))
484
- repeat = true
485
- }
486
- code , ok := awserrors .Code (err )
487
- return err == nil || (ok && code == iam .ErrCodeNoSuchEntityException )
488
- }, 5 * time .Minute , 5 * time .Second ).Should (BeTrue ())
489
- }
490
- if val .AWSCloudFormationType () == "AWS::IAM::InstanceProfile" {
485
+ iamRoles = append (iamRoles , role )
486
+ case "AWS::IAM::InstanceProfile" :
491
487
profile := val .(* cfn_iam.InstanceProfile )
492
- By (fmt .Sprintf ("cleanup for profile with name '%s'" , profile .InstanceProfileName ))
493
- repeat := false
494
- Eventually (func (gomega Gomega ) bool {
495
- _ , err := iamSvc .DeleteInstanceProfile (& iam.DeleteInstanceProfileInput {InstanceProfileName : aws .String (profile .InstanceProfileName )})
496
- if err != nil && ! repeat {
497
- By (fmt .Sprintf ("failed to delete role '%s'; reason: %+v" , profile .InstanceProfileName , err ))
498
- repeat = true
499
- }
500
- code , ok := awserrors .Code (err )
501
- return err == nil || (ok && code == iam .ErrCodeNoSuchEntityException )
502
- }, 5 * time .Minute , 5 * time .Second ).Should (BeTrue ())
503
- }
504
- if val .AWSCloudFormationType () == "AWS::IAM::ManagedPolicy" {
488
+ instanceProfiles = append (instanceProfiles , profile )
489
+ case "AWS::IAM::ManagedPolicy" :
505
490
policy := val .(* cfn_iam.ManagedPolicy )
506
- policies , err := iamSvc .ListPolicies (& iam.ListPoliciesInput {})
507
- Expect (err ).NotTo (HaveOccurred ())
508
- if len (policies .Policies ) > 0 {
509
- for _ , p := range policies .Policies {
510
- if aws .StringValue (p .PolicyName ) == policy .ManagedPolicyName {
511
- By (fmt .Sprintf ("cleanup for policy '%s'" , p .String ()))
512
- repeat := false
513
- Eventually (func (gomega Gomega ) bool {
514
- _ , err := iamSvc .DeletePolicy (& iam.DeletePolicyInput {PolicyArn : p .Arn })
515
- if err != nil && ! repeat {
516
- By (fmt .Sprintf ("failed to delete policy '%s'; reason: %+v" , policy .Description , err ))
517
- repeat = true
518
- }
519
- code , ok := awserrors .Code (err )
520
- return err == nil || (ok && code == iam .ErrCodeNoSuchEntityException )
521
- }, 5 * time .Minute , 5 * time .Second ).Should (BeTrue ())
522
- // TODO: why is there a break here? Don't we want to clean up everything?
523
- break
524
- }
491
+ policies = append (policies , policy )
492
+ case configservice .ResourceTypeAwsIamGroup :
493
+ group := val .(* cfn_iam.Group )
494
+ groups = append (groups , group )
495
+ }
496
+ }
497
+ for _ , role := range iamRoles {
498
+ By (fmt .Sprintf ("deleting the following role: %s" , role .RoleName ))
499
+ repeat := false
500
+ Eventually (func (gomega Gomega ) bool {
501
+ _ , err := iamSvc .DeleteRole (& iam.DeleteRoleInput {RoleName : aws .String (role .RoleName )})
502
+ if err != nil && ! repeat {
503
+ By (fmt .Sprintf ("failed to delete role '%s'; reason: %+v" , role .RoleName , err ))
504
+ repeat = true
505
+ }
506
+ code , ok := awserrors .Code (err )
507
+ return err == nil || (ok && code == iam .ErrCodeNoSuchEntityException )
508
+ }, 5 * time .Minute , 5 * time .Second ).Should (BeTrue ())
509
+ }
510
+ for _ , profile := range instanceProfiles {
511
+ By (fmt .Sprintf ("cleanup for profile with name '%s'" , profile .InstanceProfileName ))
512
+ repeat := false
513
+ Eventually (func (gomega Gomega ) bool {
514
+ _ , err := iamSvc .DeleteInstanceProfile (& iam.DeleteInstanceProfileInput {InstanceProfileName : aws .String (profile .InstanceProfileName )})
515
+ if err != nil && ! repeat {
516
+ By (fmt .Sprintf ("failed to delete role '%s'; reason: %+v" , profile .InstanceProfileName , err ))
517
+ repeat = true
518
+ }
519
+ code , ok := awserrors .Code (err )
520
+ return err == nil || (ok && code == iam .ErrCodeNoSuchEntityException )
521
+ }, 5 * time .Minute , 5 * time .Second ).Should (BeTrue ())
522
+ }
523
+ for _ , policy := range policies {
524
+ policies , err := iamSvc .ListPolicies (& iam.ListPoliciesInput {})
525
+ Expect (err ).NotTo (HaveOccurred ())
526
+ if len (policies .Policies ) > 0 {
527
+ for _ , p := range policies .Policies {
528
+ if aws .StringValue (p .PolicyName ) == policy .ManagedPolicyName {
529
+ By (fmt .Sprintf ("cleanup for policy '%s'" , p .String ()))
530
+ repeat := false
531
+ Eventually (func (gomega Gomega ) bool {
532
+ _ , err := iamSvc .DeletePolicy (& iam.DeletePolicyInput {PolicyArn : p .Arn })
533
+ if err != nil && ! repeat {
534
+ By (fmt .Sprintf ("failed to delete policy '%s'; reason: %+v" , policy .Description , err ))
535
+ repeat = true
536
+ }
537
+ code , ok := awserrors .Code (err )
538
+ return err == nil || (ok && code == iam .ErrCodeNoSuchEntityException )
539
+ }, 5 * time .Minute , 5 * time .Second ).Should (BeTrue ())
540
+ // TODO: why is there a break here? Don't we want to clean up everything?
541
+ break
525
542
}
526
543
}
527
544
}
528
- if val .AWSCloudFormationType () == configservice .ResourceTypeAwsIamGroup {
529
- group := val .(* cfn_iam.Group )
530
- _ , _ = iamSvc .DeleteGroup (& iam.DeleteGroupInput {GroupName : aws .String (group .GroupName )})
531
- }
545
+ }
546
+ for _ , group := range groups {
547
+ repeat := false
548
+ Eventually (func (gomega Gomega ) bool {
549
+ _ , err := iamSvc .DeleteGroup (& iam.DeleteGroupInput {GroupName : aws .String (group .GroupName )})
550
+ if err != nil && ! repeat {
551
+ By (fmt .Sprintf ("failed to delete group '%s'; reason: %+v" , group .GroupName , err ))
552
+ repeat = true
553
+ }
554
+ code , ok := awserrors .Code (err )
555
+ return err == nil || (ok && code == iam .ErrCodeNoSuchEntityException )
556
+ }, 5 * time .Minute , 5 * time .Second ).Should (BeTrue ())
532
557
}
533
558
}
534
559
0 commit comments