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

Added AWS Config Organization Conformance Pack resource #17298

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/17298.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_config_organization_conformance_pack
```
17 changes: 17 additions & 0 deletions aws/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,23 @@ func (c *Config) Client() (interface{}, error) {
return
}

// We only want to retry briefly as the default max retry count would
// excessively retry when the error could be legitimate.
// We currently depend on the DefaultRetryer exponential backoff here.
// ~10 retries gives a fair backoff of a few seconds.
if r.RetryCount < 9 {
r.Retryable = aws.Bool(true)
} else {
r.Retryable = aws.Bool(false)
}
case "DeleteOrganizationConformancePack", "DescribeOrganizationConformancePacks", "DescribeOrganizationConformancePackStatuses", "PutOrganizationConformancePack":
if !tfawserr.ErrCodeEquals(r.Error, configservice.ErrCodeOrganizationAccessDeniedException) {
if r.Operation.Name == "DeleteOrganizationConformancePack" && tfawserr.ErrCodeEquals(err, configservice.ErrCodeResourceInUseException) {
r.Retryable = aws.Bool(true)
}
return
}

// We only want to retry briefly as the default max retry count would
// excessively retry when the error could be legitimate.
// We currently depend on the DefaultRetryer exponential backoff here.
Expand Down
203 changes: 203 additions & 0 deletions aws/configservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ const (
ConfigConformancePackCreateTimeout = 5 * time.Minute
ConfigConformancePackDeleteTimeout = 5 * time.Minute

ConfigOrganizationConformancePackCreateTimeout = 10 * time.Minute
ConfigOrganizationConformancePackUpdateTimeout = 10 * time.Minute
ConfigOrganizationConformancePackDeleteTimeout = 20 * time.Minute

ConfigConformancePackStatusNotFound = "NotFound"
ConfigConformancePackStatusUnknown = "Unknown"
)
Expand Down Expand Up @@ -135,6 +139,62 @@ func configDescribeOrganizationConfigRuleStatus(conn *configservice.ConfigServic
return nil, nil
}

func configDescribeOrganizationConformancePack(conn *configservice.ConfigService, name string) (*configservice.OrganizationConformancePack, error) {
input := &configservice.DescribeOrganizationConformancePacksInput{
OrganizationConformancePackNames: []*string{aws.String(name)},
}

for {
output, err := conn.DescribeOrganizationConformancePacks(input)

if err != nil {
return nil, err
}

for _, pack := range output.OrganizationConformancePacks {
if aws.StringValue(pack.OrganizationConformancePackName) == name {
return pack, nil
}
}

if aws.StringValue(output.NextToken) == "" {
break
}

input.NextToken = output.NextToken
}

return nil, nil
}

func configDescribeOrganizationConformancePackStatus(conn *configservice.ConfigService, name string) (*configservice.OrganizationConformancePackStatus, error) {
input := &configservice.DescribeOrganizationConformancePackStatusesInput{
OrganizationConformancePackNames: []*string{aws.String(name)},
}

for {
output, err := conn.DescribeOrganizationConformancePackStatuses(input)

if err != nil {
return nil, err
}

for _, status := range output.OrganizationConformancePackStatuses {
if aws.StringValue(status.OrganizationConformancePackName) == name {
return status, nil
}
}

if aws.StringValue(output.NextToken) == "" {
break
}

input.NextToken = output.NextToken
}

return nil, nil
}

func configGetOrganizationConfigRuleDetailedStatus(conn *configservice.ConfigService, ruleName, ruleStatus string) ([]*configservice.MemberAccountStatus, error) {
input := &configservice.GetOrganizationConfigRuleDetailedStatusInput{
Filters: &configservice.StatusDetailFilters{
Expand Down Expand Up @@ -163,6 +223,35 @@ func configGetOrganizationConfigRuleDetailedStatus(conn *configservice.ConfigSer
return statuses, nil
}

func configGetOrganizationConformancePackDetailedStatus(conn *configservice.ConfigService, name, status string) ([]*configservice.OrganizationConformancePackDetailedStatus, error) {
input := &configservice.GetOrganizationConformancePackDetailedStatusInput{
Filters: &configservice.OrganizationResourceDetailedStatusFilters{
Status: aws.String(status),
},
OrganizationConformancePackName: aws.String(name),
}

var statuses []*configservice.OrganizationConformancePackDetailedStatus

for {
output, err := conn.GetOrganizationConformancePackDetailedStatus(input)

if err != nil {
return nil, err
}

statuses = append(statuses, output.OrganizationConformancePackDetailedStatuses...)

if aws.StringValue(output.NextToken) == "" {
break
}

input.NextToken = output.NextToken
}

return statuses, nil
}

func configRefreshConformancePackStatus(conn *configservice.ConfigService, name string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
status, err := configDescribeConformancePackStatus(conn, name)
Expand Down Expand Up @@ -221,6 +310,78 @@ func configRefreshOrganizationConfigRuleStatus(conn *configservice.ConfigService
}
}

func configRefreshOrganizationConformancePackCreationStatus(conn *configservice.ConfigService, name string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
status, err := configDescribeOrganizationConformancePackStatus(conn, name)

// Transient ResourceDoesNotExist error after creation caught here
// in cases where the StateChangeConf's delay time is not sufficient
if tfawserr.ErrCodeEquals(err, configservice.ErrCodeNoSuchOrganizationConformancePackException) {
return nil, "", nil
}

if err != nil {
return nil, "", err
}

if status == nil {
return nil, "", nil
}

if status.ErrorCode != nil {
return status, aws.StringValue(status.Status), fmt.Errorf("%s: %s", aws.StringValue(status.ErrorCode), aws.StringValue(status.ErrorMessage))
}

switch s := aws.StringValue(status.Status); s {
case configservice.OrganizationResourceStatusCreateFailed, configservice.OrganizationResourceStatusDeleteFailed, configservice.OrganizationResourceStatusUpdateFailed:
return status, s, configOrganizationConformancePackDetailedStatusError(conn, name, s)
}

return status, aws.StringValue(status.Status), nil
}
}

func configRefreshOrganizationConformancePackStatus(conn *configservice.ConfigService, name string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
status, err := configDescribeOrganizationConformancePackStatus(conn, name)

if err != nil {
return nil, "", err
}

if status == nil {
return nil, "", nil
}

if status.ErrorCode != nil {
return status, aws.StringValue(status.Status), fmt.Errorf("%s: %s", aws.StringValue(status.ErrorCode), aws.StringValue(status.ErrorMessage))
}

switch s := aws.StringValue(status.Status); s {
case configservice.OrganizationResourceStatusCreateFailed, configservice.OrganizationResourceStatusDeleteFailed, configservice.OrganizationResourceStatusUpdateFailed:
return status, s, configOrganizationConformancePackDetailedStatusError(conn, name, s)
}

return status, aws.StringValue(status.Status), nil
}
}

func configOrganizationConformancePackDetailedStatusError(conn *configservice.ConfigService, name, status string) error {
memberAccountStatuses, err := configGetOrganizationConformancePackDetailedStatus(conn, name, status)

if err != nil {
return fmt.Errorf("unable to get Config Organization Conformance Pack detailed status for showing member account errors: %w", err)
}

var errBuilder strings.Builder

for _, mas := range memberAccountStatuses {
errBuilder.WriteString(fmt.Sprintf("Account ID (%s): %s: %s\n", aws.StringValue(mas.AccountId), aws.StringValue(mas.ErrorCode), aws.StringValue(mas.ErrorMessage)))
}

return fmt.Errorf("Failed in %d account(s):\n\n%s", len(memberAccountStatuses), errBuilder.String())
}

func configWaitForConformancePackStateCreateComplete(conn *configservice.ConfigService, name string) error {
stateChangeConf := resource.StateChangeConf{
Pending: []string{configservice.ConformancePackStateCreateInProgress},
Expand Down Expand Up @@ -256,6 +417,48 @@ func configWaitForConformancePackStateDeleteComplete(conn *configservice.ConfigS
return err
}

func configWaitForOrganizationConformancePackStatusCreateSuccessful(conn *configservice.ConfigService, name string) error {
stateChangeConf := resource.StateChangeConf{
Pending: []string{configservice.OrganizationResourceStatusCreateInProgress},
Target: []string{configservice.OrganizationResourceStatusCreateSuccessful},
Timeout: ConfigOrganizationConformancePackCreateTimeout,
Refresh: configRefreshOrganizationConformancePackCreationStatus(conn, name),
// Include a delay to help avoid ResourceDoesNotExist errors
Delay: 30 * time.Second,
}

_, err := stateChangeConf.WaitForState()

return err

}

func configWaitForOrganizationConformancePackStatusUpdateSuccessful(conn *configservice.ConfigService, name string) error {
stateChangeConf := resource.StateChangeConf{
Pending: []string{configservice.OrganizationResourceStatusUpdateInProgress},
Target: []string{configservice.OrganizationResourceStatusUpdateSuccessful},
Timeout: ConfigOrganizationConformancePackUpdateTimeout,
Refresh: configRefreshOrganizationConformancePackStatus(conn, name),
}

_, err := stateChangeConf.WaitForState()

return err
}

func configWaitForOrganizationConformancePackStatusDeleteSuccessful(conn *configservice.ConfigService, name string) error {
stateChangeConf := resource.StateChangeConf{
Pending: []string{configservice.OrganizationResourceStatusDeleteInProgress},
Target: []string{configservice.OrganizationResourceStatusDeleteSuccessful},
Timeout: ConfigOrganizationConformancePackDeleteTimeout,
Refresh: configRefreshOrganizationConformancePackStatus(conn, name),
}

_, err := stateChangeConf.WaitForState()

return err
}

func configWaitForOrganizationRuleStatusCreateSuccessful(conn *configservice.ConfigService, name string, timeout time.Duration) error {
stateChangeConf := &resource.StateChangeConf{
Pending: []string{configservice.OrganizationRuleStatusCreateInProgress},
Expand Down
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ func Provider() *schema.Provider {
"aws_config_configuration_recorder_status": resourceAwsConfigConfigurationRecorderStatus(),
"aws_config_conformance_pack": resourceAwsConfigConformancePack(),
"aws_config_delivery_channel": resourceAwsConfigDeliveryChannel(),
"aws_config_organization_conformance_pack": resourceAwsConfigOrganizationConformancePack(),
"aws_config_organization_custom_rule": resourceAwsConfigOrganizationCustomRule(),
"aws_config_organization_managed_rule": resourceAwsConfigOrganizationManagedRule(),
"aws_config_remediation_configuration": resourceAwsConfigRemediationConfiguration(),
Expand Down
Loading