Skip to content

Commit

Permalink
Merge pull request #24057 from DrFaust92/configservice-policy
Browse files Browse the repository at this point in the history
r/config_config_rule - use finder + add custom policy
  • Loading branch information
ewbankkit authored Apr 12, 2022
2 parents 45b980a + 5204f36 commit ef5034f
Show file tree
Hide file tree
Showing 9 changed files with 363 additions and 143 deletions.
11 changes: 11 additions & 0 deletions .changelog/24057.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
```release-note:enhancement
resource/aws_config_config_rule: Add `source.custom_policy_details` argument.
```

```release-note:enhancement
resource/aws_config_config_rule: Add plan time validation for `source.source_detail.event_source` and `source.source_detail.message_type`.
```

```release-note:enhancement
resource/aws_config_config_rule: Make `source.source_identifier` optional.
```
149 changes: 67 additions & 82 deletions internal/service/configservice/config_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import (
"bytes"
"fmt"
"log"
"regexp"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/configservice"
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
Expand Down Expand Up @@ -58,7 +59,7 @@ func ResourceConfigRule() *schema.Resource {
"maximum_execution_frequency": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validExecutionFrequency(),
ValidateFunc: validation.StringInSlice(configservice.MaximumExecutionFrequency_Values(), false),
},
"scope": {
Type: schema.TypeList,
Expand Down Expand Up @@ -100,13 +101,43 @@ func ResourceConfigRule() *schema.Resource {
Required: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"custom_policy_details": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"enable_debug_log_delivery": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"policy_runtime": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.All(
validation.StringLenBetween(0, 64),
validation.StringMatch(regexp.MustCompile(`^guard\-2\.x\.x$`), "Must match cloudformation-guard version"),
),
},
"policy_text": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringLenBetween(0, 10000),
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { // policy_text always returns empty
if d.Id() != "" && old == "" {
return true
}
return false
},
},
},
},
},
"owner": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
configservice.OwnerCustomLambda,
configservice.OwnerAws,
}, false),
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice(configservice.Owner_Values(), false),
},
"source_detail": {
Type: schema.TypeSet,
Expand All @@ -116,25 +147,27 @@ func ResourceConfigRule() *schema.Resource {
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"event_source": {
Type: schema.TypeString,
Optional: true,
Default: "aws.config",
Type: schema.TypeString,
Optional: true,
Default: "aws.config",
ValidateFunc: validation.StringInSlice(configservice.EventSource_Values(), false),
},
"maximum_execution_frequency": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validExecutionFrequency(),
ValidateFunc: validation.StringInSlice(configservice.MaximumExecutionFrequency_Values(), false),
},
"message_type": {
Type: schema.TypeString,
Optional: true,
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice(configservice.MessageType_Values(), false),
},
},
},
},
"source_identifier": {
Type: schema.TypeString,
Required: true,
Optional: true,
ValidateFunc: validation.StringLenBetween(0, 256),
},
},
Expand Down Expand Up @@ -178,14 +211,12 @@ func resourceRulePutConfig(d *schema.ResourceData, meta interface{}) error {
err := resource.Retry(tfiam.PropagationTimeout, func() *resource.RetryError {
_, err := conn.PutConfigRule(&input)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "InsufficientPermissionsException" {
// IAM is eventually consistent
return resource.RetryableError(err)
}
if tfawserr.ErrCodeEquals(err, configservice.ErrCodeInsufficientPermissionsException) {
// IAM is eventually consistent
return resource.RetryableError(err)
}

return resource.NonRetryableError(fmt.Errorf("Failed to create AWSConfig rule: %s", err))
return resource.NonRetryableError(fmt.Errorf("Failed to create AWSConfig rule: %w", err))
}

return nil
Expand All @@ -194,18 +225,19 @@ func resourceRulePutConfig(d *schema.ResourceData, meta interface{}) error {
_, err = conn.PutConfigRule(&input)
}
if err != nil {
return fmt.Errorf("Error creating AWSConfig rule: %s", err)
return fmt.Errorf("Error creating AWSConfig rule: %w", err)
}

d.SetId(name)

log.Printf("[DEBUG] AWSConfig config rule %q created", name)

if !d.IsNewResource() && d.HasChange("tags_all") {
arn := d.Get("arn").(string)
o, n := d.GetChange("tags_all")

if err := UpdateTags(conn, d.Get("arn").(string), o, n); err != nil {
return fmt.Errorf("error updating Config Config Rule (%s) tags: %s", d.Get("arn").(string), err)
if err := UpdateTags(conn, arn, o, n); err != nil {
return fmt.Errorf("error updating Config Config Rule (%s) tags: %w", arn, err)
}
}

Expand All @@ -217,34 +249,16 @@ func resourceConfigRuleRead(d *schema.ResourceData, meta interface{}) error {
defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig
ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig

out, err := conn.DescribeConfigRules(&configservice.DescribeConfigRulesInput{
ConfigRuleNames: []*string{aws.String(d.Id())},
})
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoSuchConfigRuleException" {
log.Printf("[WARN] Config Rule %q is gone (NoSuchConfigRuleException)", d.Id())
d.SetId("")
return nil
}
return err
}
rule, err := FindConfigRule(conn, d.Id())

numberOfRules := len(out.ConfigRules)
if numberOfRules < 1 {
log.Printf("[WARN] Config Rule %q is gone (no rules found)", d.Id())
if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] ConfigService Config Rule (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if numberOfRules > 1 {
return fmt.Errorf("Expected exactly 1 Config Rule, received %d: %#v",
numberOfRules, out.ConfigRules)
}

log.Printf("[DEBUG] AWS Config config rule received: %s", out)

rule := out.ConfigRules[0]
d.Set("arn", rule.ConfigRuleArn)
arn := aws.StringValue(rule.ConfigRuleArn)
d.Set("arn", arn)
d.Set("rule_id", rule.ConfigRuleId)
d.Set("name", rule.ConfigRuleName)
d.Set("description", rule.Description)
Expand All @@ -257,10 +271,10 @@ func resourceConfigRuleRead(d *schema.ResourceData, meta interface{}) error {

d.Set("source", flattenRuleSource(rule.Source))

tags, err := ListTags(conn, d.Get("arn").(string))
tags, err := ListTags(conn, arn)

if err != nil {
return fmt.Errorf("error listing tags for Config Config Rule (%s): %s", d.Get("arn").(string), err)
return fmt.Errorf("error listing tags for Config Config Rule (%s): %w", arn, err)
}

tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig)
Expand Down Expand Up @@ -289,7 +303,7 @@ func resourceConfigRuleDelete(d *schema.ResourceData, meta interface{}) error {
err := resource.Retry(2*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteConfigRule(input)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "ResourceInUseException" {
if tfawserr.ErrCodeEquals(err, configservice.ErrCodeResourceInUseException) {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
Expand All @@ -300,42 +314,13 @@ func resourceConfigRuleDelete(d *schema.ResourceData, meta interface{}) error {
_, err = conn.DeleteConfigRule(input)
}
if err != nil {
return fmt.Errorf("Deleting Config Rule failed: %s", err)
return fmt.Errorf("error Deleting Config Rule failed: %w", err)
}

conf := resource.StateChangeConf{
Pending: []string{
configservice.ConfigRuleStateActive,
configservice.ConfigRuleStateDeleting,
configservice.ConfigRuleStateDeletingResults,
configservice.ConfigRuleStateEvaluating,
},
Target: []string{""},
Timeout: 5 * time.Minute,
Refresh: func() (interface{}, string, error) {
out, err := conn.DescribeConfigRules(&configservice.DescribeConfigRulesInput{
ConfigRuleNames: []*string{aws.String(d.Id())},
})
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoSuchConfigRuleException" {
return 42, "", nil
}
return 42, "", fmt.Errorf("Failed to describe config rule %q: %s", d.Id(), err)
}
if len(out.ConfigRules) < 1 {
return 42, "", nil
}
rule := out.ConfigRules[0]
return out, *rule.ConfigRuleState, nil
},
}
_, err = conf.WaitForState()
if err != nil {
return err
if _, err := waitRuleDeleted(conn, d.Id()); err != nil {
return fmt.Errorf("error waiting for Config Service Rule (%s) to deleted: %w", d.Id(), err)
}

log.Printf("[DEBUG] AWS Config config rule %q deleted", name)

return nil
}

Expand Down
Loading

0 comments on commit ef5034f

Please sign in to comment.