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

New Resources: aws_config_organization_custom_rule and aws_config_organization_managed_rule #9716

Merged
merged 3 commits into from
Aug 12, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
23 changes: 23 additions & 0 deletions aws/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,29 @@ func (c *Config) Client() (interface{}, error) {
}
})

client.configconn.Handlers.Retry.PushBack(func(r *request.Request) {
// When calling Config Organization Rules API actions immediately
// after Organization creation, the API can randomly return the
// OrganizationAccessDeniedException error for a few minutes, even
// after succeeding a few requests.
switch r.Operation.Name {
case "DeleteOrganizationConfigRule", "DescribeOrganizationConfigRules", "DescribeOrganizationConfigRuleStatuses", "PutOrganizationConfigRule":
if !isAWSErr(r.Error, configservice.ErrCodeOrganizationAccessDeniedException, "This action can be only made by AWS Organization's master account.") {
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)
}
}
})

// See https://github.com/aws/aws-sdk-go/pull/1276
client.dynamodbconn.Handlers.Retry.PushBack(func(r *request.Request) {
if r.Operation.Name != "PutItem" && r.Operation.Name != "UpdateItem" && r.Operation.Name != "DeleteItem" {
Expand Down
179 changes: 179 additions & 0 deletions aws/configservice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package aws

import (
"fmt"
"strings"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/configservice"
"github.com/hashicorp/terraform/helper/resource"
)

func configDescribeOrganizationConfigRule(conn *configservice.ConfigService, name string) (*configservice.OrganizationConfigRule, error) {
input := &configservice.DescribeOrganizationConfigRulesInput{
OrganizationConfigRuleNames: []*string{aws.String(name)},
}

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

if err != nil {
return nil, err
}

for _, rule := range output.OrganizationConfigRules {
if aws.StringValue(rule.OrganizationConfigRuleName) == name {
return rule, nil
}
}

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

input.NextToken = output.NextToken
}

return nil, nil
}

func configDescribeOrganizationConfigRuleStatus(conn *configservice.ConfigService, name string) (*configservice.OrganizationConfigRuleStatus, error) {
input := &configservice.DescribeOrganizationConfigRuleStatusesInput{
OrganizationConfigRuleNames: []*string{aws.String(name)},
}

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

if err != nil {
return nil, err
}

for _, status := range output.OrganizationConfigRuleStatuses {
if aws.StringValue(status.OrganizationConfigRuleName) == 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{
MemberAccountRuleStatus: aws.String(ruleStatus),
},
OrganizationConfigRuleName: aws.String(ruleName),
}
var statuses []*configservice.MemberAccountStatus

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

if err != nil {
return nil, err
}

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

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

input.NextToken = output.NextToken
}

return statuses, nil
}

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

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

if status == nil {
return nil, "", fmt.Errorf("status not found")
}

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

switch aws.StringValue(status.OrganizationRuleStatus) {
case configservice.OrganizationRuleStatusCreateFailed, configservice.OrganizationRuleStatusDeleteFailed, configservice.OrganizationRuleStatusUpdateFailed:
// Display detailed errors for failed member accounts
memberAccountStatuses, err := configGetOrganizationConfigRuleDetailedStatus(conn, name, aws.StringValue(status.OrganizationRuleStatus))

if err != nil {
return status, aws.StringValue(status.OrganizationRuleStatus), fmt.Errorf("unable to get Organization Config Rule detailed status for showing member account errors: %s", 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 status, aws.StringValue(status.OrganizationRuleStatus), fmt.Errorf("Failed in %d account(s):\n\n%s", len(memberAccountStatuses), errBuilder.String())
}

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

func configWaitForOrganizationRuleStatusCreateSuccessful(conn *configservice.ConfigService, name string, timeout time.Duration) error {
stateChangeConf := &resource.StateChangeConf{
Pending: []string{configservice.OrganizationRuleStatusCreateInProgress},
Target: []string{configservice.OrganizationRuleStatusCreateSuccessful},
Refresh: configRefreshOrganizationConfigRuleStatus(conn, name),
Timeout: timeout,
Delay: 10 * time.Second,
}

_, err := stateChangeConf.WaitForState()

return err
}

func configWaitForOrganizationRuleStatusDeleteSuccessful(conn *configservice.ConfigService, name string, timeout time.Duration) error {
stateChangeConf := &resource.StateChangeConf{
Pending: []string{configservice.OrganizationRuleStatusDeleteInProgress},
Target: []string{configservice.OrganizationRuleStatusDeleteSuccessful},
Refresh: configRefreshOrganizationConfigRuleStatus(conn, name),
Timeout: timeout,
Delay: 10 * time.Second,
}

_, err := stateChangeConf.WaitForState()

if isAWSErr(err, configservice.ErrCodeNoSuchOrganizationConfigRuleException, "") {
return nil
}

return err
}

func configWaitForOrganizationRuleStatusUpdateSuccessful(conn *configservice.ConfigService, name string, timeout time.Duration) error {
stateChangeConf := &resource.StateChangeConf{
Pending: []string{configservice.OrganizationRuleStatusUpdateInProgress},
Target: []string{configservice.OrganizationRuleStatusUpdateSuccessful},
Refresh: configRefreshOrganizationConfigRuleStatus(conn, name),
Timeout: timeout,
Delay: 10 * time.Second,
}

_, err := stateChangeConf.WaitForState()

return err
}
2 changes: 2 additions & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,8 @@ func Provider() terraform.ResourceProvider {
"aws_config_configuration_recorder": resourceAwsConfigConfigurationRecorder(),
"aws_config_configuration_recorder_status": resourceAwsConfigConfigurationRecorderStatus(),
"aws_config_delivery_channel": resourceAwsConfigDeliveryChannel(),
"aws_config_organization_custom_rule": resourceAwsConfigOrganizationCustomRule(),
"aws_config_organization_managed_rule": resourceAwsConfigOrganizationManagedRule(),
"aws_cognito_identity_pool": resourceAwsCognitoIdentityPool(),
"aws_cognito_identity_pool_roles_attachment": resourceAwsCognitoIdentityPoolRolesAttachment(),
"aws_cognito_identity_provider": resourceAwsCognitoIdentityProvider(),
Expand Down
Loading