package aws import ( "bytes" "fmt" "log" "time" "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/configservice" ) func resourceAwsConfigConfigRule() *schema.Resource { return &schema.Resource{ Create: resourceAwsConfigConfigRulePut, Read: resourceAwsConfigConfigRuleRead, Update: resourceAwsConfigConfigRulePut, Delete: resourceAwsConfigConfigRuleDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, Required: true, ValidateFunc: validateMaxLength(64), }, "rule_id": { Type: schema.TypeString, Computed: true, }, "arn": { Type: schema.TypeString, Computed: true, }, "description": { Type: schema.TypeString, Optional: true, ValidateFunc: validateMaxLength(256), }, "input_parameters": { Type: schema.TypeString, Optional: true, ValidateFunc: validateJsonString, }, "maximum_execution_frequency": { Type: schema.TypeString, Optional: true, ValidateFunc: validateConfigExecutionFrequency, }, "scope": { Type: schema.TypeList, MaxItems: 1, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "compliance_resource_id": { Type: schema.TypeString, Optional: true, ValidateFunc: validateMaxLength(256), }, "compliance_resource_types": { Type: schema.TypeSet, Optional: true, MaxItems: 100, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validateMaxLength(256), }, Set: schema.HashString, }, "tag_key": { Type: schema.TypeString, Optional: true, ValidateFunc: validateMaxLength(128), }, "tag_value": { Type: schema.TypeString, Optional: true, ValidateFunc: validateMaxLength(256), }, }, }, }, "source": { Type: schema.TypeList, MaxItems: 1, Required: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "owner": { Type: schema.TypeString, Required: true, ValidateFunc: validateConfigRuleSourceOwner, }, "source_detail": { Type: schema.TypeSet, Set: configRuleSourceDetailsHash, Optional: true, MaxItems: 25, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "event_source": { Type: schema.TypeString, Optional: true, }, "maximum_execution_frequency": { Type: schema.TypeString, Optional: true, ValidateFunc: validateConfigExecutionFrequency, }, "message_type": { Type: schema.TypeString, Optional: true, }, }, }, }, "source_identifier": { Type: schema.TypeString, Required: true, ValidateFunc: validateMaxLength(256), }, }, }, }, }, } } func resourceAwsConfigConfigRulePut(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).configconn name := d.Get("name").(string) ruleInput := configservice.ConfigRule{ ConfigRuleName: aws.String(name), Source: expandConfigRuleSource(d.Get("source").([]interface{})), } scopes := d.Get("scope").([]interface{}) if len(scopes) > 0 { ruleInput.Scope = expandConfigRuleScope(scopes[0].(map[string]interface{})) } if v, ok := d.GetOk("description"); ok { ruleInput.Description = aws.String(v.(string)) } if v, ok := d.GetOk("input_parameters"); ok { ruleInput.InputParameters = aws.String(v.(string)) } if v, ok := d.GetOk("maximum_execution_frequency"); ok { ruleInput.MaximumExecutionFrequency = aws.String(v.(string)) } input := configservice.PutConfigRuleInput{ ConfigRule: &ruleInput, } log.Printf("[DEBUG] Creating AWSConfig config rule: %s", input) err := resource.Retry(2*time.Minute, 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) } } return resource.NonRetryableError(fmt.Errorf("Failed to create AWSConfig rule: %s", err)) } return nil }) if err != nil { return err } d.SetId(name) log.Printf("[DEBUG] AWSConfig config rule %q created", name) return resourceAwsConfigConfigRuleRead(d, meta) } func resourceAwsConfigConfigRuleRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).configconn 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 } numberOfRules := len(out.ConfigRules) if numberOfRules < 1 { log.Printf("[WARN] Config Rule %q is gone (no rules found)", 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) d.Set("rule_id", rule.ConfigRuleId) d.Set("name", rule.ConfigRuleName) d.Set("description", rule.Description) d.Set("input_parameters", rule.InputParameters) d.Set("maximum_execution_frequency", rule.MaximumExecutionFrequency) if rule.Scope != nil { d.Set("scope", flattenConfigRuleScope(rule.Scope)) } d.Set("source", flattenConfigRuleSource(rule.Source)) return nil } func resourceAwsConfigConfigRuleDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).configconn name := d.Get("name").(string) log.Printf("[DEBUG] Deleting AWS Config config rule %q", name) _, err := conn.DeleteConfigRule(&configservice.DeleteConfigRuleInput{ ConfigRuleName: aws.String(name), }) if err != nil { return fmt.Errorf("Deleting Config Rule failed: %s", 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 } log.Printf("[DEBUG] AWS Config config rule %q deleted", name) d.SetId("") return nil } func configRuleSourceDetailsHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) if v, ok := m["message_type"]; ok { buf.WriteString(fmt.Sprintf("%s-", v.(string))) } if v, ok := m["event_source"]; ok { buf.WriteString(fmt.Sprintf("%s-", v.(string))) } if v, ok := m["maximum_execution_frequency"]; ok { buf.WriteString(fmt.Sprintf("%s-", v.(string))) } return hashcode.String(buf.String()) }