From a618b048cf232000327db590df7cb2b4a0049b19 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 27 Aug 2015 23:11:56 +0100 Subject: [PATCH 1/3] aws: Add support for aws_cloudtrail --- builtin/providers/aws/config.go | 5 + builtin/providers/aws/provider.go | 1 + .../providers/aws/resource_aws_cloudtrail.go | 167 ++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_cloudtrail.go diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index b8fc9fa47..298d24ccd 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -13,6 +13,7 @@ import ( "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/service/autoscaling" "github.com/aws/aws-sdk-go/service/cloudformation" + "github.com/aws/aws-sdk-go/service/cloudtrail" "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/aws/aws-sdk-go/service/codedeploy" @@ -51,6 +52,7 @@ type Config struct { type AWSClient struct { cfconn *cloudformation.CloudFormation + cloudtrailconn *cloudtrail.CloudTrail cloudwatchconn *cloudwatch.CloudWatch cloudwatchlogsconn *cloudwatchlogs.CloudWatchLogs dsconn *directoryservice.DirectoryService @@ -188,6 +190,9 @@ func (c *Config) Client() (interface{}, error) { log.Println("[INFO] Initializing CloudWatch SDK connection") client.cloudwatchconn = cloudwatch.New(awsConfig) + log.Println("[INFO] Initializing CloudTrail connection") + client.cloudtrailconn = cloudtrail.New(awsConfig) + log.Println("[INFO] Initializing CloudWatch Logs connection") client.cloudwatchlogsconn = cloudwatchlogs.New(awsConfig) diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 5b02d4a70..d4567e238 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -164,6 +164,7 @@ func Provider() terraform.ResourceProvider { "aws_autoscaling_notification": resourceAwsAutoscalingNotification(), "aws_autoscaling_policy": resourceAwsAutoscalingPolicy(), "aws_cloudformation_stack": resourceAwsCloudFormationStack(), + "aws_cloudtrail": resourceAwsCloudTrail(), "aws_cloudwatch_log_group": resourceAwsCloudWatchLogGroup(), "aws_autoscaling_lifecycle_hook": resourceAwsAutoscalingLifecycleHook(), "aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(), diff --git a/builtin/providers/aws/resource_aws_cloudtrail.go b/builtin/providers/aws/resource_aws_cloudtrail.go new file mode 100644 index 000000000..eed420edf --- /dev/null +++ b/builtin/providers/aws/resource_aws_cloudtrail.go @@ -0,0 +1,167 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudtrail" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsCloudTrail() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsCloudTrailCreate, + Read: resourceAwsCloudTrailRead, + Update: resourceAwsCloudTrailUpdate, + Delete: resourceAwsCloudTrailDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "s3_bucket_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "s3_key_prefix": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "cloud_watch_logs_role_arn": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "cloud_watch_logs_group_arn": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "include_global_service_events": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "sns_topic_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func resourceAwsCloudTrailCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cloudtrailconn + + input := cloudtrail.CreateTrailInput{ + Name: aws.String(d.Get("name").(string)), + S3BucketName: aws.String(d.Get("s3_bucket_name").(string)), + } + + if v, ok := d.GetOk("cloud_watch_logs_group_arn"); ok { + input.CloudWatchLogsLogGroupArn = aws.String(v.(string)) + } + if v, ok := d.GetOk("cloud_watch_logs_role_arn"); ok { + input.CloudWatchLogsRoleArn = aws.String(v.(string)) + } + if v, ok := d.GetOk("include_global_service_events"); ok { + input.IncludeGlobalServiceEvents = aws.Bool(v.(bool)) + } + if v, ok := d.GetOk("s3_key_prefix"); ok { + input.S3KeyPrefix = aws.String(v.(string)) + } + if v, ok := d.GetOk("sns_topic_name"); ok { + input.SnsTopicName = aws.String(v.(string)) + } + + t, err := conn.CreateTrail(&input) + if err != nil { + return err + } + + log.Printf("[DEBUG] CloudTrail created: %s", t) + + d.SetId(*t.Name) + + return resourceAwsCloudTrailRead(d, meta) +} + +func resourceAwsCloudTrailRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cloudtrailconn + + name := d.Get("name").(string) + input := cloudtrail.DescribeTrailsInput{ + TrailNameList: []*string{ + aws.String(name), + }, + } + resp, err := conn.DescribeTrails(&input) + if err != nil { + return err + } + if len(resp.TrailList) == 0 { + return fmt.Errorf("No CloudTrail found, using name %q", name) + } + + trail := resp.TrailList[0] + log.Printf("[DEBUG] CloudTrail received: %s", trail) + + d.Set("name", trail.Name) + d.Set("s3_bucket_name", trail.S3BucketName) + d.Set("s3_key_prefix", trail.S3KeyPrefix) + d.Set("cloud_watch_logs_role_arn", trail.CloudWatchLogsRoleArn) + d.Set("cloud_watch_logs_group_arn", trail.CloudWatchLogsLogGroupArn) + d.Set("include_global_service_events", trail.IncludeGlobalServiceEvents) + d.Set("sns_topic_name", trail.SnsTopicName) + + return nil +} + +func resourceAwsCloudTrailUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cloudtrailconn + + input := cloudtrail.UpdateTrailInput{ + Name: aws.String(d.Get("name").(string)), + } + + if d.HasChange("s3_bucket_name") { + input.S3BucketName = aws.String(d.Get("s3_bucket_name").(string)) + } + if d.HasChange("s3_key_prefix") { + input.S3KeyPrefix = aws.String(d.Get("s3_key_prefix").(string)) + } + if d.HasChange("cloud_watch_logs_role_arn") { + input.CloudWatchLogsRoleArn = aws.String(d.Get("cloud_watch_logs_role_arn").(string)) + } + if d.HasChange("cloud_watch_logs_group_arn") { + input.CloudWatchLogsLogGroupArn = aws.String(d.Get("cloud_watch_logs_group_arn").(string)) + } + if d.HasChange("include_global_service_events") { + input.IncludeGlobalServiceEvents = aws.Bool(d.Get("include_global_service_events").(bool)) + } + if d.HasChange("sns_topic_name") { + input.SnsTopicName = aws.String(d.Get("sns_topic_name").(string)) + } + + log.Printf("[DEBUG] Updating CloudTrail: %s", input) + t, err := conn.UpdateTrail(&input) + if err != nil { + return err + } + log.Printf("[DEBUG] CloudTrail updated: %s", t) + + return resourceAwsCloudTrailRead(d, meta) +} + +func resourceAwsCloudTrailDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cloudtrailconn + name := d.Get("name").(string) + + log.Printf("[DEBUG] Deleting CloudTrail: %q", name) + _, err := conn.DeleteTrail(&cloudtrail.DeleteTrailInput{ + Name: aws.String(name), + }) + + return err +} From 7265bdaaf0cefc261bbc144acf1c09968c48943a Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Thu, 27 Aug 2015 23:12:12 +0100 Subject: [PATCH 2/3] aws: Add acceptance test for aws_cloudtrail --- .../aws/resource_aws_cloudtrail_test.go | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_cloudtrail_test.go diff --git a/builtin/providers/aws/resource_aws_cloudtrail_test.go b/builtin/providers/aws/resource_aws_cloudtrail_test.go new file mode 100644 index 000000000..10ed17a5b --- /dev/null +++ b/builtin/providers/aws/resource_aws_cloudtrail_test.go @@ -0,0 +1,169 @@ +package aws + +import ( + "fmt" + "math/rand" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudtrail" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSCloudTrail_basic(t *testing.T) { + var trail cloudtrail.Trail + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCloudTrailDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSCloudTrailConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudTrailExists("aws_cloudtrail.foobar", &trail), + resource.TestCheckResourceAttr("aws_cloudtrail.foobar", "include_global_service_events", "true"), + ), + }, + resource.TestStep{ + Config: testAccAWSCloudTrailConfigModified, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudTrailExists("aws_cloudtrail.foobar", &trail), + resource.TestCheckResourceAttr("aws_cloudtrail.foobar", "s3_key_prefix", "/prefix"), + resource.TestCheckResourceAttr("aws_cloudtrail.foobar", "include_global_service_events", "false"), + ), + }, + }, + }) +} + +func testAccCheckCloudTrailExists(n string, trail *cloudtrail.Trail) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + conn := testAccProvider.Meta().(*AWSClient).cloudtrailconn + params := cloudtrail.DescribeTrailsInput{ + TrailNameList: []*string{aws.String(rs.Primary.ID)}, + } + resp, err := conn.DescribeTrails(¶ms) + if err != nil { + return err + } + if len(resp.TrailList) == 0 { + return fmt.Errorf("Trail not found") + } + *trail = *resp.TrailList[0] + + return nil + } +} + +func testAccCheckAWSCloudTrailDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).cloudtrailconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_cloudtrail" { + continue + } + + params := cloudtrail.DescribeTrailsInput{ + TrailNameList: []*string{aws.String(rs.Primary.ID)}, + } + + resp, err := conn.DescribeTrails(¶ms) + + if err == nil { + if len(resp.TrailList) != 0 && + *resp.TrailList[0].Name == rs.Primary.ID { + return fmt.Errorf("CloudTrail still exists: %s", rs.Primary.ID) + } + } + } + + return nil +} + +var cloudTrailRandInt = rand.New(rand.NewSource(time.Now().UnixNano())).Int() + +var testAccAWSCloudTrailConfig = fmt.Sprintf(` +resource "aws_cloudtrail" "foobar" { + name = "tf-trail-foobar" + s3_bucket_name = "${aws_s3_bucket.foo.id}" +} + +resource "aws_s3_bucket" "foo" { + bucket = "tf-test-trail-%d" + force_destroy = true + policy = < Date: Thu, 27 Aug 2015 23:25:29 +0100 Subject: [PATCH 3/3] aws: Add docs for aws_cloudtrail --- .../providers/aws/r/cloudtrail.html.markdown | 75 +++++++++++++++++++ website/source/layouts/aws.erb | 9 +++ 2 files changed, 84 insertions(+) create mode 100644 website/source/docs/providers/aws/r/cloudtrail.html.markdown diff --git a/website/source/docs/providers/aws/r/cloudtrail.html.markdown b/website/source/docs/providers/aws/r/cloudtrail.html.markdown new file mode 100644 index 000000000..d4ba604fc --- /dev/null +++ b/website/source/docs/providers/aws/r/cloudtrail.html.markdown @@ -0,0 +1,75 @@ +--- +layout: "aws" +page_title: "AWS: cloudtrail" +sidebar_current: "docs-aws-resource-cloudtrail" +description: |- + Provides a CloudTrail resource. +--- + +# aws\_cloudtrail + +Provides a CloudTrail resource. + +## Example Usage +``` +resource "aws_cloudtrail" "foobar" { + name = "tf-trail-foobar" + s3_bucket_name = "${aws_s3_bucket.foo.id}" + s3_key_prefix = "/prefix" + include_global_service_events = false +} + +resource "aws_s3_bucket" "foo" { + bucket = "tf-test-trail" + force_destroy = true + policy = < + > + CloudTrail Resources + + + > CloudWatch Resources