diff --git a/aws/resource_aws_cloudtrail.go b/aws/resource_aws_cloudtrail.go index d35a20a1195..9f904b9f13d 100644 --- a/aws/resource_aws_cloudtrail.go +++ b/aws/resource_aws_cloudtrail.go @@ -133,6 +133,19 @@ func resourceAwsCloudTrail() *schema.Resource { Computed: true, }, "tags": tagsSchema(), + "insight_selector": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "insight_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(cloudtrail.InsightType_Values(), false), + }, + }, + }, + }, }, } } @@ -219,6 +232,12 @@ func resourceAwsCloudTrailCreate(d *schema.ResourceData, meta interface{}) error } } + if _, ok := d.GetOk("insight_selector"); ok { + if err := cloudTrailSetInsightSelectors(conn, d); err != nil { + return err + } + } + return resourceAwsCloudTrailRead(d, meta) } @@ -300,6 +319,21 @@ func resourceAwsCloudTrailRead(d *schema.ResourceData, meta interface{}) error { return err } + // Get InsightSelectors + insightSelectors, err := conn.GetInsightSelectors(&cloudtrail.GetInsightSelectorsInput{ + TrailName: aws.String(d.Id()), + }) + if err != nil { + if !isAWSErr(err, cloudtrail.ErrCodeInsightNotEnabledException, "") { + return fmt.Errorf("error getting Cloud Trail (%s) Insight Selectors: %w", d.Id(), err) + } + } + if insightSelectors != nil { + if err := d.Set("insight_selector", flattenAwsCloudTrailInsightSelector(insightSelectors.InsightSelectors)); err != nil { + return err + } + } + return nil } @@ -387,6 +421,13 @@ func resourceAwsCloudTrailUpdate(d *schema.ResourceData, meta interface{}) error } } + if !d.IsNewResource() && d.HasChange("insight_selector") { + log.Printf("[DEBUG] Updating insight selector on CloudTrail: %s", input) + if err := cloudTrailSetInsightSelectors(conn, d); err != nil { + return err + } + } + log.Printf("[DEBUG] CloudTrail updated: %s", t) return resourceAwsCloudTrailRead(d, meta) @@ -548,3 +589,51 @@ func flattenAwsCloudTrailEventSelectorDataResource(configured []*cloudtrail.Data return dataResources } + +func cloudTrailSetInsightSelectors(conn *cloudtrail.CloudTrail, d *schema.ResourceData) error { + input := &cloudtrail.PutInsightSelectorsInput{ + TrailName: aws.String(d.Id()), + } + + insightSelector := expandAwsCloudTrailInsightSelector(d.Get("insight_selector").([]interface{})) + input.InsightSelectors = insightSelector + + if err := input.Validate(); err != nil { + return fmt.Errorf("Error validate CloudTrail (%s): %s", d.Id(), err) + } + + _, err := conn.PutInsightSelectors(input) + if err != nil { + return fmt.Errorf("Error set insight selector on CloudTrail (%s): %s", d.Id(), err) + } + + return nil +} + +func expandAwsCloudTrailInsightSelector(configured []interface{}) []*cloudtrail.InsightSelector { + insightSelectors := make([]*cloudtrail.InsightSelector, 0, len(configured)) + + for _, raw := range configured { + data := raw.(map[string]interface{}) + + is := &cloudtrail.InsightSelector{ + InsightType: aws.String(data["insight_type"].(string)), + } + insightSelectors = append(insightSelectors, is) + } + + return insightSelectors +} + +func flattenAwsCloudTrailInsightSelector(configured []*cloudtrail.InsightSelector) []map[string]interface{} { + insightSelectors := make([]map[string]interface{}, 0, len(configured)) + + for _, raw := range configured { + item := make(map[string]interface{}) + item["insight_type"] = aws.StringValue(raw.InsightType) + + insightSelectors = append(insightSelectors, item) + } + + return insightSelectors +} diff --git a/aws/resource_aws_cloudtrail_test.go b/aws/resource_aws_cloudtrail_test.go index 1a8884453ac..2d40efd2e44 100644 --- a/aws/resource_aws_cloudtrail_test.go +++ b/aws/resource_aws_cloudtrail_test.go @@ -104,6 +104,7 @@ func TestAccAWSCloudTrail_serial(t *testing.T) { "kmsKey": testAccAWSCloudTrail_kmsKey, "tags": testAccAWSCloudTrail_tags, "eventSelector": testAccAWSCloudTrail_event_selector, + "insightSelector": testAccAWSCloudTrail_insight_selector, }, } @@ -545,6 +546,31 @@ func testAccAWSCloudTrail_event_selector(t *testing.T) { }) } +func testAccAWSCloudTrail_insight_selector(t *testing.T) { + resourceName := "aws_cloudtrail.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCloudTrailDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudTrailConfig_insightSelector(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "insight_selector.#", "1"), + resource.TestCheckResourceAttr(resourceName, "insight_selector.0.insight_type", "ApiCallRateInsight"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckCloudTrailExists(n string, trail *cloudtrail.Trail) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -672,7 +698,7 @@ func testAccAWSCloudTrailConfig(cloudTrailRandInt int) string { return fmt.Sprintf(` resource "aws_cloudtrail" "foobar" { name = "tf-trail-foobar-%d" - s3_bucket_name = "${aws_s3_bucket.foo.id}" + s3_bucket_name = aws_s3_bucket.foo.id } resource "aws_s3_bucket" "foo" { @@ -1543,3 +1569,49 @@ POLICY } `, cloudTrailRandInt, cloudTrailRandInt, cloudTrailRandInt, cloudTrailRandInt) } + +func testAccAWSCloudTrailConfig_insightSelector(rName string) string { + return fmt.Sprintf(` +resource "aws_cloudtrail" "test" { + name = %[1]q + s3_bucket_name = aws_s3_bucket.test.id + + + insight_selector { + insight_type = "ApiCallRateInsight" + } +} + +resource "aws_s3_bucket" "test" { + bucket = %[1]q + force_destroy = true + + policy = <