From f5220ab884dd040fd9e6fc27d86d9140985aa99b Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 2 Feb 2017 22:32:09 +0000 Subject: [PATCH] provider/aws: Add aws_config_configuration_recorder --- builtin/providers/aws/provider.go | 1 + ...ource_aws_config_configuration_recorder.go | 148 +++++++++ ..._aws_config_configuration_recorder_test.go | 304 ++++++++++++++++++ builtin/providers/aws/structure.go | 36 +++ ...onfig_configuration_recorder.html.markdown | 76 +++++ website/source/layouts/aws.erb | 4 + 6 files changed, 569 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_config_configuration_recorder.go create mode 100644 builtin/providers/aws/resource_aws_config_configuration_recorder_test.go create mode 100644 website/source/docs/providers/aws/r/config_configuration_recorder.html.markdown diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 528af49e2..1471051d3 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -232,6 +232,7 @@ func Provider() terraform.ResourceProvider { "aws_cloudwatch_log_stream": resourceAwsCloudWatchLogStream(), "aws_cloudwatch_log_subscription_filter": resourceAwsCloudwatchLogSubscriptionFilter(), "aws_config_config_rule": resourceAwsConfigConfigRule(), + "aws_config_configuration_recorder": resourceAwsConfigConfigurationRecorder(), "aws_autoscaling_lifecycle_hook": resourceAwsAutoscalingLifecycleHook(), "aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(), "aws_codedeploy_app": resourceAwsCodeDeployApp(), diff --git a/builtin/providers/aws/resource_aws_config_configuration_recorder.go b/builtin/providers/aws/resource_aws_config_configuration_recorder.go new file mode 100644 index 000000000..c635d63db --- /dev/null +++ b/builtin/providers/aws/resource_aws_config_configuration_recorder.go @@ -0,0 +1,148 @@ +package aws + +import ( + "fmt" + "log" + + "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 resourceAwsConfigConfigurationRecorder() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsConfigConfigurationRecorderPut, + Read: resourceAwsConfigConfigurationRecorderRead, + Update: resourceAwsConfigConfigurationRecorderPut, + Delete: resourceAwsConfigConfigurationRecorderDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + Default: "default", + ValidateFunc: validateMaxLength(256), + }, + "role_arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateArn, + }, + "recording_group": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "all_supported": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "include_global_resource_types": { + Type: schema.TypeBool, + Optional: true, + }, + "resource_types": { + Type: schema.TypeSet, + Set: schema.HashString, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + }, + } +} + +func resourceAwsConfigConfigurationRecorderPut(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).configconn + + name := d.Get("name").(string) + recorder := configservice.ConfigurationRecorder{ + Name: aws.String(name), + RoleARN: aws.String(d.Get("role_arn").(string)), + } + + if g, ok := d.GetOk("recording_group"); ok { + recorder.RecordingGroup = expandConfigRecordingGroup(g.([]interface{})) + } + + input := configservice.PutConfigurationRecorderInput{ + ConfigurationRecorder: &recorder, + } + _, err := conn.PutConfigurationRecorder(&input) + if err != nil { + return fmt.Errorf("Creating Configuration Recorder failed: %s", err) + } + + d.SetId(name) + + return resourceAwsConfigConfigurationRecorderRead(d, meta) +} + +func resourceAwsConfigConfigurationRecorderRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).configconn + + input := configservice.DescribeConfigurationRecordersInput{ + ConfigurationRecorderNames: []*string{aws.String(d.Id())}, + } + out, err := conn.DescribeConfigurationRecorders(&input) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoSuchConfigurationRecorderException" { + log.Printf("[WARN] Configuration Recorder %q is gone (NoSuchConfigurationRecorderException)", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("Getting Configuration Recorder failed: %s", err) + } + + numberOfRecorders := len(out.ConfigurationRecorders) + if numberOfRecorders < 1 { + log.Printf("[WARN] Configuration Recorder %q is gone (no recorders found)", d.Id()) + d.SetId("") + return nil + } + + if numberOfRecorders > 1 { + return fmt.Errorf("Expected exactly 1 Configuration Recorder, received %d: %#v", + numberOfRecorders, out.ConfigurationRecorders) + } + + recorder := out.ConfigurationRecorders[0] + + d.Set("name", recorder.Name) + d.Set("role_arn", recorder.RoleARN) + + if recorder.RecordingGroup != nil { + flattened := flattenConfigRecordingGroup(recorder.RecordingGroup) + err = d.Set("recording_group", flattened) + if err != nil { + return fmt.Errorf("Failed to set recording_group: %s", err) + } + } + + return nil +} + +func resourceAwsConfigConfigurationRecorderDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).configconn + input := configservice.DeleteConfigurationRecorderInput{ + ConfigurationRecorderName: aws.String(d.Id()), + } + _, err := conn.DeleteConfigurationRecorder(&input) + if err != nil { + return fmt.Errorf("Deleting Configuration Recorder failed: %s", err) + } + + d.SetId("") + return nil +} diff --git a/builtin/providers/aws/resource_aws_config_configuration_recorder_test.go b/builtin/providers/aws/resource_aws_config_configuration_recorder_test.go new file mode 100644 index 000000000..8a7363d18 --- /dev/null +++ b/builtin/providers/aws/resource_aws_config_configuration_recorder_test.go @@ -0,0 +1,304 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/configservice" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSConfigConfigurationRecorder_basic(t *testing.T) { + var cr configservice.ConfigurationRecorder + rInt := acctest.RandInt() + expectedName := fmt.Sprintf("tf-acc-test-%d", rInt) + expectedRoleName := fmt.Sprintf("tf-acc-test-awsconfig-%d", rInt) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigConfigurationRecorderDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigConfigurationRecorderConfig_basic(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigConfigurationRecorderExists("aws_config_configuration_recorder.foo", &cr), + testAccCheckConfigConfigurationRecorderName("aws_config_configuration_recorder.foo", expectedName, &cr), + testAccCheckConfigConfigurationRecorderRoleArn("aws_config_configuration_recorder.foo", + regexp.MustCompile(`arn:aws:iam::[0-9]{12}:role/`+expectedRoleName), &cr), + resource.TestCheckResourceAttr("aws_config_configuration_recorder.foo", "name", expectedName), + resource.TestMatchResourceAttr("aws_config_configuration_recorder.foo", "role_arn", + regexp.MustCompile(`arn:aws:iam::[0-9]{12}:role/`+expectedRoleName)), + ), + }, + }, + }) +} + +func TestAccAWSConfigConfigurationRecorder_allParams(t *testing.T) { + var cr configservice.ConfigurationRecorder + rInt := acctest.RandInt() + expectedName := fmt.Sprintf("tf-acc-test-%d", rInt) + expectedRoleName := fmt.Sprintf("tf-acc-test-awsconfig-%d", rInt) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigConfigurationRecorderDestroy, + Steps: []resource.TestStep{ + { + Config: testAccConfigConfigurationRecorderConfig_allParams(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigConfigurationRecorderExists("aws_config_configuration_recorder.foo", &cr), + testAccCheckConfigConfigurationRecorderName("aws_config_configuration_recorder.foo", expectedName, &cr), + testAccCheckConfigConfigurationRecorderRoleArn("aws_config_configuration_recorder.foo", + regexp.MustCompile(`arn:aws:iam::[0-9]{12}:role/`+expectedRoleName), &cr), + resource.TestCheckResourceAttr("aws_config_configuration_recorder.foo", "name", expectedName), + resource.TestMatchResourceAttr("aws_config_configuration_recorder.foo", "role_arn", + regexp.MustCompile(`arn:aws:iam::[0-9]{12}:role/`+expectedRoleName)), + resource.TestCheckResourceAttr("aws_config_configuration_recorder.foo", "recording_group.#", "1"), + resource.TestCheckResourceAttr("aws_config_configuration_recorder.foo", "recording_group.0.all_supported", "false"), + resource.TestCheckResourceAttr("aws_config_configuration_recorder.foo", "recording_group.0.include_global_resource_types", "false"), + resource.TestCheckResourceAttr("aws_config_configuration_recorder.foo", "recording_group.0.resource_types.#", "2"), + ), + }, + }, + }) +} + +func TestAccAWSConfigConfigurationRecorder_importBasic(t *testing.T) { + resourceName := "aws_config_configuration_recorder.foo" + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckConfigConfigurationRecorderDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccConfigConfigurationRecorderConfig_basic(rInt), + }, + + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckConfigConfigurationRecorderName(n string, desired string, obj *configservice.ConfigurationRecorder) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if *obj.Name != desired { + return fmt.Errorf("Expected configuration recorder %q name to be %q, given: %q", + n, desired, *obj.Name) + } + + return nil + } +} + +func testAccCheckConfigConfigurationRecorderRoleArn(n string, desired *regexp.Regexp, obj *configservice.ConfigurationRecorder) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if !desired.MatchString(*obj.RoleARN) { + return fmt.Errorf("Expected configuration recorder %q role ARN to match %q, given: %q", + n, desired.String(), *obj.RoleARN) + } + + return nil + } +} + +func testAccCheckConfigConfigurationRecorderExists(n string, obj *configservice.ConfigurationRecorder) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not Found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No configuration recorder ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).configconn + out, err := conn.DescribeConfigurationRecorders(&configservice.DescribeConfigurationRecordersInput{ + ConfigurationRecorderNames: []*string{aws.String(rs.Primary.Attributes["name"])}, + }) + if err != nil { + return fmt.Errorf("Failed to describe configuration recorder: %s", err) + } + if len(out.ConfigurationRecorders) < 1 { + return fmt.Errorf("No configuration recorder found when describing %q", rs.Primary.Attributes["name"]) + } + + cr := out.ConfigurationRecorders[0] + *obj = *cr + + return nil + } +} + +func testAccCheckConfigConfigurationRecorderDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).configconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_config_configuration_recorder_status" { + continue + } + + resp, err := conn.DescribeConfigurationRecorders(&configservice.DescribeConfigurationRecordersInput{ + ConfigurationRecorderNames: []*string{aws.String(rs.Primary.Attributes["name"])}, + }) + + if err == nil { + if len(resp.ConfigurationRecorders) != 0 && + *resp.ConfigurationRecorders[0].Name == rs.Primary.Attributes["name"] { + return fmt.Errorf("Configuration recorder still exists: %s", rs.Primary.Attributes["name"]) + } + } + } + + return nil +} + +func testAccConfigConfigurationRecorderConfig_basic(randInt int) string { + return fmt.Sprintf(` +resource "aws_config_configuration_recorder" "foo" { + name = "tf-acc-test-%d" + role_arn = "${aws_iam_role.r.arn}" +} + +resource "aws_iam_role" "r" { + name = "tf-acc-test-awsconfig-%d" + assume_role_policy = < 0 { + m["resource_types"] = schema.NewSet(schema.HashString, flattenStringList(g.ResourceTypes)) + } + + return []map[string]interface{}{m} +} + func pointersMapToStringList(pointers map[string]*string) map[string]interface{} { list := make(map[string]interface{}, len(pointers)) for i, v := range pointers { diff --git a/website/source/docs/providers/aws/r/config_configuration_recorder.html.markdown b/website/source/docs/providers/aws/r/config_configuration_recorder.html.markdown new file mode 100644 index 000000000..82e60b8c6 --- /dev/null +++ b/website/source/docs/providers/aws/r/config_configuration_recorder.html.markdown @@ -0,0 +1,76 @@ +--- +layout: "aws" +page_title: "AWS: aws_config_configuration_recorder" +sidebar_current: "docs-aws-resource-config-configuration-recorder" +description: |- + Provides an AWS Config Configuration Recorder. +--- + +# aws\_config\_configuration\_recorder + +Provides an AWS Config Configuration Recorder. Please note that this resource **does not start** the created recorder automatically. + +~> **Note:** _Starting_ the Configuration Recorder requires a [delivery channel](/docs/providers/aws/r/config_delivery_channel.html) (while delivery channel creation requires Configuration Recorder). This is why [`aws_config_configuration_recorder_status`](/docs/providers/aws/r/config_configuration_recorder_status.html) is a separate resource. + +## Example Usage + +``` +resource "aws_config_configuration_recorder" "foo" { + name = "example" + role_arn = "${aws_iam_role.r.arn}" +} + +resource "aws_iam_role" "r" { + name = "awsconfig-example" + assume_role_policy = <aws_config_config_rule + > + aws_config_configuration_recorder + +