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

r/aws_cloudformation_stack_set_instance: Empty OU refactor #24523

Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3641b9e
Treat Deployment Targets as their own case
sbutler May 3, 2022
9360d70
Merge remote-tracking branch 'upstream/main' into b-aws_cloudformatio…
sbutler May 3, 2022
55a8f42
Adjust to change in func names
sbutler May 3, 2022
7e6d0c6
Add changelog for PR
sbutler May 3, 2022
7aa203f
Handle deletes with multipe deployment_targets
sbutler May 6, 2022
d2f3d26
Merge branch 'main' into b-aws_cloudformation_stack_set_instance-empt…
sbutler May 9, 2022
dc7fb87
Merge branch 'main' into b-aws_cloudformation_stack_set_instance-empt…
sbutler May 10, 2022
c7cbd9b
Merge branch 'main' into b-aws_cloudformation_stack_set_instance-empt…
sbutler Jul 26, 2022
1a74a0d
Merge branch 'main' into b-aws_cloudformation_stack_set_instance-empt…
sbutler May 1, 2023
5010468
Merge branch 'main' into b-aws_cloudformation_stack_set_instance-empt…
jar-b Jul 18, 2023
0b8edd0
r/aws_cloudformation_stack_set_instance: fix build failures
jar-b Jul 18, 2023
c6965be
r/aws_cloudformation_stack_set_instance: improve support for OU deplo…
jar-b Jul 19, 2023
a720599
r/aws_cloudformation_stack_set_instance(test): improve support for OU…
jar-b Jul 19, 2023
07650d1
r/aws_cloudformation_stack_set_instance(doc): document stack_instance…
jar-b Jul 19, 2023
6b58e00
chore: add changelog entries
jar-b Jul 20, 2023
1af297d
r/aws_cloudformation_stack_set_instance: fix linter findings
jar-b Jul 20, 2023
ea0379c
r/aws_cloudformation_stack_set_instance(doc): fix attribute order
jar-b Jul 20, 2023
3d428e7
r/aws_cloudformation_stack_set_instance(doc): update id description, …
jar-b Jul 20, 2023
dfa7583
Merge branch 'main' into b-aws_cloudformation_stack_set_instance-empt…
jar-b Jul 20, 2023
20059ef
chore: adjust changelog
jar-b Jul 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/24523.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
resource/aws_cloudformation_stack_set_instance: Refactor to better support deployment targets.
```
47 changes: 47 additions & 0 deletions internal/service/cloudformation/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,53 @@ func FindStackByID(conn *cloudformation.CloudFormation, id string) (*cloudformat
return stack, nil
}

func FindStackInstanceSummariesByOrgIDs(conn *cloudformation.CloudFormation, stackSetName, region, callAs string, orgIDs []string) ([]*cloudformation.StackInstanceSummary, error) {
input := &cloudformation.ListStackInstancesInput{
StackInstanceRegion: aws.String(region),
StackSetName: aws.String(stackSetName),
}

if callAs != "" {
input.CallAs = aws.String(callAs)
}

var result []*cloudformation.StackInstanceSummary

err := conn.ListStackInstancesPages(input, func(page *cloudformation.ListStackInstancesOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, s := range page.Summaries {
if s == nil {
continue
}

for _, orgID := range orgIDs {
if aws.StringValue(s.OrganizationalUnitId) == orgID {
result = append(result, s)
}
}

}

return !lastPage
})

if tfawserr.ErrCodeEquals(err, cloudformation.ErrCodeStackSetNotFoundException) {
return nil, &resource.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

return result, nil
}

func FindStackInstanceAccountIdByOrgIDs(conn *cloudformation.CloudFormation, stackSetName, region, callAs string, orgIDs []string) (string, error) {
input := &cloudformation.ListStackInstancesInput{
StackInstanceRegion: aws.String(region),
Expand Down
88 changes: 60 additions & 28 deletions internal/service/cloudformation/stack_set_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ func ResourceStackSetInstance() *schema.Resource {
ValidateFunc: verify.ValidAccountID,
ConflictsWith: []string{"deployment_targets"},
},
"account_ids": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"call_as": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -141,6 +146,11 @@ func ResourceStackSetInstance() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"stack_ids": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"stack_set_name": {
Type: schema.TypeString,
Required: true,
Expand Down Expand Up @@ -259,48 +269,70 @@ func resourceStackSetInstanceRead(d *schema.ResourceData, meta interface{}) erro
conn := meta.(*conns.AWSClient).CloudFormationConn

stackSetName, accountID, region, err := StackSetInstanceParseResourceID(d.Id())

callAs := d.Get("call_as").(string)

if err != nil {
return err
}

// Determine correct account ID for the Instance if created with deployment targets;
// we only expect the accountID to be the organization root ID or organizational unit (OU) IDs
// separated by a slash after creation.
if regexp.MustCompile(`(ou-[a-z0-9]{4,32}-[a-z0-9]{8,32}|r-[a-z0-9]{4,32})`).MatchString(accountID) {
orgIDs := strings.Split(accountID, "/")
accountID, err = FindStackInstanceAccountIdByOrgIDs(conn, stackSetName, region, callAs, orgIDs)
accountIDIsDT := regexp.MustCompile(`(ou-[a-z0-9]{4,32}-[a-z0-9]{8,32}|r-[a-z0-9]{4,32})`).MatchString(accountID)
callAs := d.Get("call_as").(string)

d.Set("region", region)
d.Set("stack_set_name", stackSetName)

if v, ok := d.GetOk("deployment_targets"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
// Process this as a deployment target instance
orgIDs := make([]string, 0, len(v.([]interface{})))
for _, orgID := range expandDeploymentTargets(v.([]interface{})).OrganizationalUnitIds {
orgIDs = append(orgIDs, *orgID)
}
if !accountIDIsDT {
accountID = strings.Join(orgIDs, "/")
d.SetId(StackSetInstanceCreateResourceID(stackSetName, accountID, region))
}

summaries, err := FindStackInstanceSummariesByOrgIDs(conn, stackSetName, region, callAs, orgIDs)

if err != nil {
return fmt.Errorf("error finding CloudFormation StackSet Instance (%s) Account: %w", d.Id(), err)
}

d.SetId(StackSetInstanceCreateResourceID(stackSetName, accountID, region))
}
accountIDs := make([]*string, 0, len(summaries))
stackIDs := make([]*string, 0, len(summaries))
for _, si := range summaries {
accountIDs = append(accountIDs, si.Account)
stackIDs = append(stackIDs, si.StackId)
}
d.Set("account_ids", accountIDs)
d.Set("stack_ids", stackIDs)
} else if v, ok := d.GetOk("account_id"); ok && v != nil {
// Process this as an account ID instance
if accountIDIsDT {
accountID = v.(string)
d.SetId(StackSetInstanceCreateResourceID(stackSetName, accountID, region))
}

stackInstance, err := FindStackInstanceByName(conn, stackSetName, accountID, region, callAs)
stackInstance, err := FindStackInstanceByName(conn, stackSetName, accountID, region, callAs)

if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] CloudFormation StackSet Instance (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] CloudFormation StackSet Instance (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error reading CloudFormation StackSet Instance (%s): %w", d.Id(), err)
}
if err != nil {
return fmt.Errorf("error reading CloudFormation StackSet Instance (%s): %w", d.Id(), err)
}

d.Set("account_id", stackInstance.Account)
d.Set("organizational_unit_id", stackInstance.OrganizationalUnitId)
if err := d.Set("parameter_overrides", flattenAllParameters(stackInstance.ParameterOverrides)); err != nil {
return fmt.Errorf("error setting parameters: %w", err)
}
d.Set("account_id", stackInstance.Account)
d.Set("organizational_unit_id", stackInstance.OrganizationalUnitId)
if err := d.Set("parameter_overrides", flattenAllParameters(stackInstance.ParameterOverrides)); err != nil {
return fmt.Errorf("error setting parameters: %w", err)
}

d.Set("region", stackInstance.Region)
d.Set("stack_id", stackInstance.StackId)
d.Set("stack_set_name", stackSetName)
d.Set("stack_id", stackInstance.StackId)
} else {
return fmt.Errorf("error reading CloudFormation StackSet Instance (%s): no account_id or deployment_targets", d.Id())
}

return nil
}
Expand Down