From 69230acbb630946cb089e40b8c5648715906a56f Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Thu, 9 Nov 2017 21:34:10 -0500 Subject: [PATCH] New Resource: aws_cloudwatch_log_resource_policy --- aws/provider.go | 1 + ...urce_aws_cloudwatch_log_resource_policy.go | 118 +++++++++++++ ...aws_cloudwatch_log_resource_policy_test.go | 160 ++++++++++++++++++ aws/validators.go | 12 ++ website/aws.erb | 4 + ...oudwatch_log_resource_policy.html.markdown | 59 +++++++ 6 files changed, 354 insertions(+) create mode 100644 aws/resource_aws_cloudwatch_log_resource_policy.go create mode 100644 aws/resource_aws_cloudwatch_log_resource_policy_test.go create mode 100644 website/docs/r/cloudwatch_log_resource_policy.html.markdown diff --git a/aws/provider.go b/aws/provider.go index 24e92e486cc..f90ad0be369 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -276,6 +276,7 @@ func Provider() terraform.ResourceProvider { "aws_cloudwatch_log_destination_policy": resourceAwsCloudWatchLogDestinationPolicy(), "aws_cloudwatch_log_group": resourceAwsCloudWatchLogGroup(), "aws_cloudwatch_log_metric_filter": resourceAwsCloudWatchLogMetricFilter(), + "aws_cloudwatch_log_resource_policy": resourceAwsCloudWatchLogResourcePolicy(), "aws_cloudwatch_log_stream": resourceAwsCloudWatchLogStream(), "aws_cloudwatch_log_subscription_filter": resourceAwsCloudwatchLogSubscriptionFilter(), "aws_config_config_rule": resourceAwsConfigConfigRule(), diff --git a/aws/resource_aws_cloudwatch_log_resource_policy.go b/aws/resource_aws_cloudwatch_log_resource_policy.go new file mode 100644 index 00000000000..7e7ceb8b5aa --- /dev/null +++ b/aws/resource_aws_cloudwatch_log_resource_policy.go @@ -0,0 +1,118 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsCloudWatchLogResourcePolicy() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsCloudWatchLogResourcePolicyPut, + Read: resourceAwsCloudWatchLogResourcePolicyRead, + Update: resourceAwsCloudWatchLogResourcePolicyPut, + Delete: resourceAwsCloudWatchLogResourcePolicyDelete, + + Importer: &schema.ResourceImporter{ + State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + d.Set("policy_name", d.Id()) + return []*schema.ResourceData{d}, nil + }, + }, + + Schema: map[string]*schema.Schema{ + "policy_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "policy_document": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateCloudWatchLogResourcePolicyDocument, + DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs, + }, + }, + } +} + +func resourceAwsCloudWatchLogResourcePolicyPut(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cloudwatchlogsconn + + policyName := d.Get("policy_name").(string) + + input := &cloudwatchlogs.PutResourcePolicyInput{ + PolicyDocument: aws.String(d.Get("policy_document").(string)), + PolicyName: aws.String(policyName), + } + + log.Printf("[DEBUG] Writing CloudWatch log resource policy: %#v", input) + _, err := conn.PutResourcePolicy(input) + + if err != nil { + return fmt.Errorf("Writing CloudWatch log resource policy failed: %s", err.Error()) + } + + d.SetId(policyName) + return resourceAwsCloudWatchLogResourcePolicyRead(d, meta) +} + +func resourceAwsCloudWatchLogResourcePolicyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cloudwatchlogsconn + policyName := d.Get("policy_name").(string) + resourcePolicy, exists, err := lookupCloudWatchLogResourcePolicy(conn, policyName, nil) + if err != nil { + return err + } + + if !exists { + d.SetId("") + return nil + } + + d.SetId(policyName) + d.Set("policy_document", *resourcePolicy.PolicyDocument) + + return nil +} + +func resourceAwsCloudWatchLogResourcePolicyDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).cloudwatchlogsconn + input := cloudwatchlogs.DeleteResourcePolicyInput{ + PolicyName: aws.String(d.Id()), + } + + log.Printf("[DEBUG] Deleting CloudWatch log resource policy: %#v", input) + _, err := conn.DeleteResourcePolicy(&input) + if err != nil { + return fmt.Errorf("Deleting CloudWatch log resource policy '%s' failed: %s", *input.PolicyName, err.Error()) + } + return nil +} + +func lookupCloudWatchLogResourcePolicy(conn *cloudwatchlogs.CloudWatchLogs, + name string, nextToken *string) (*cloudwatchlogs.ResourcePolicy, bool, error) { + input := &cloudwatchlogs.DescribeResourcePoliciesInput{ + NextToken: nextToken, + } + log.Printf("[DEBUG] Reading CloudWatch log resource policies: %#v", input) + resp, err := conn.DescribeResourcePolicies(input) + if err != nil { + return nil, true, err + } + + for _, resourcePolicy := range resp.ResourcePolicies { + if *resourcePolicy.PolicyName == name { + return resourcePolicy, true, nil + } + } + + if resp.NextToken != nil { + return lookupCloudWatchLogResourcePolicy(conn, name, resp.NextToken) + } + + return nil, false, nil +} diff --git a/aws/resource_aws_cloudwatch_log_resource_policy_test.go b/aws/resource_aws_cloudwatch_log_resource_policy_test.go new file mode 100644 index 00000000000..777635d07f7 --- /dev/null +++ b/aws/resource_aws_cloudwatch_log_resource_policy_test.go @@ -0,0 +1,160 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSCloudWatchLogResourcePolicy_Basic(t *testing.T) { + name := acctest.RandString(5) + var resourcePolicy cloudwatchlogs.ResourcePolicy + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudWatchLogResourcePolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckAWSCloudWatchLogResourcePolicyResourceConfigBasic1(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchLogResourcePolicy("aws_cloudwatch_log_resource_policy.test", &resourcePolicy), + resource.TestCheckResourceAttr("aws_cloudwatch_log_resource_policy.test", "policy_name", name), + resource.TestCheckResourceAttr("aws_cloudwatch_log_resource_policy.test", "policy_document", "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"route53.amazonaws.com\"},\"Action\":[\"logs:PutLogEvents\",\"logs:CreateLogStream\"],\"Resource\":\"arn:aws:logs:*:*:log-group:/aws/route53/*\"}]}"), + ), + }, + resource.TestStep{ + Config: testAccCheckAWSCloudWatchLogResourcePolicyResourceConfigBasic2(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchLogResourcePolicy("aws_cloudwatch_log_resource_policy.test", &resourcePolicy), + resource.TestCheckResourceAttr("aws_cloudwatch_log_resource_policy.test", "policy_name", name), + resource.TestCheckResourceAttr("aws_cloudwatch_log_resource_policy.test", "policy_document", "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"route53.amazonaws.com\"},\"Action\":[\"logs:PutLogEvents\",\"logs:CreateLogStream\"],\"Resource\":\"arn:aws:logs:*:*:log-group:/aws/route53/example.com\"}]}"), + ), + }, + }, + }) +} + +func TestAccAWSCloudWatchLogResourcePolicy_Import(t *testing.T) { + resourceName := "aws_cloudwatch_log_resource_policy.test" + + name := acctest.RandString(5) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudWatchLogResourcePolicyDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckAWSCloudWatchLogResourcePolicyResourceConfigBasic1(name), + }, + + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckCloudWatchLogResourcePolicy(pr string, resourcePolicy *cloudwatchlogs.ResourcePolicy) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn + rs, ok := s.RootModule().Resources[pr] + if !ok { + return fmt.Errorf("Not found: %s", pr) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + policy, exists, err := lookupCloudWatchLogResourcePolicy(conn, rs.Primary.ID, nil) + if err != nil { + return err + } + if !exists { + return fmt.Errorf("Resource policy does not exist: %q", rs.Primary.ID) + } + + *resourcePolicy = *policy + + return nil + } +} + +func testAccCheckCloudWatchLogResourcePolicyDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_cloudwatch_log_resource_policy" { + continue + } + + _, exists, err := lookupCloudWatchLogResourcePolicy(conn, rs.Primary.ID, nil) + if err != nil { + return nil + } + + if exists { + return fmt.Errorf("Resource policy exists: %q", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAWSCloudWatchLogResourcePolicyResourceConfigBasic1(name string) string { + return fmt.Sprintf(` +data "aws_iam_policy_document" "test" { + statement { + actions = [ + "logs:CreateLogStream", + "logs:PutLogEvents", + ] + + resources = ["arn:aws:logs:*:*:log-group:/aws/route53/*"] + + principals { + identifiers = ["route53.amazonaws.com"] + type = "Service" + } + } +} + +resource "aws_cloudwatch_log_resource_policy" "test" { + policy_name = "%s" + policy_document = "${data.aws_iam_policy_document.test.json}" +} +`, name) +} + +func testAccCheckAWSCloudWatchLogResourcePolicyResourceConfigBasic2(name string) string { + return fmt.Sprintf(` +data "aws_iam_policy_document" "test" { + statement { + actions = [ + "logs:CreateLogStream", + "logs:PutLogEvents", + ] + + resources = ["arn:aws:logs:*:*:log-group:/aws/route53/example.com"] + + principals { + identifiers = ["route53.amazonaws.com"] + type = "Service" + } + } +} + +resource "aws_cloudwatch_log_resource_policy" "test" { + policy_name = "%s" + policy_document = "${data.aws_iam_policy_document.test.json}" +} +`, name) +} diff --git a/aws/validators.go b/aws/validators.go index 991a7070cc9..e0dea6d36f4 100644 --- a/aws/validators.go +++ b/aws/validators.go @@ -281,6 +281,18 @@ func validateCloudWatchEventRuleName(v interface{}, k string) (ws []string, erro return } +func validateCloudWatchLogResourcePolicyDocument(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + // http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutResourcePolicy.html + if len(value) > 5120 || (len(value) == 0) { + errors = append(errors, fmt.Errorf("CloudWatch log resource policy document must be between 1 and 5120 characters.")) + } + if _, err := normalizeJsonString(v); err != nil { + errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) + } + return +} + func validateMaxLength(length int) schema.SchemaValidateFunc { return func(v interface{}, k string) (ws []string, errors []error) { value := v.(string) diff --git a/website/aws.erb b/website/aws.erb index 5b47e6de12d..6301c09f4af 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -381,6 +381,10 @@ aws_cloudwatch_log_metric_filter + > + aws_cloudwatch_log_resource_policy + + > aws_cloudwatch_log_stream diff --git a/website/docs/r/cloudwatch_log_resource_policy.html.markdown b/website/docs/r/cloudwatch_log_resource_policy.html.markdown new file mode 100644 index 00000000000..fef369ee37d --- /dev/null +++ b/website/docs/r/cloudwatch_log_resource_policy.html.markdown @@ -0,0 +1,59 @@ +--- +layout: "aws" +page_title: "AWS: aws_cloudwatch_log_resource_policy" +sidebar_current: "docs-aws-resource-cloudwatch-log-resource-policy" +description: |- + Provides a resource to manage a CloudWatch log resource policy +--- + +# aws_cloudwatch_log_resource_policy + +Provides a resource to manage a CloudWatch log resource policy. + +## Example Usage + +### Route53 Query Logging + +```hcl +data "aws_iam_policy_document" "route53-query-logging-policy" { + statement { + actions = [ + "logs:CreateLogStream", + "logs:PutLogEvents", + ] + + resources = ["arn:aws:logs:*:*:log-group:/aws/route53/*"] + + principals { + identifiers = ["route53.amazonaws.com"] + type = "Service" + } + } +} + +resource "aws_cloudwatch_log_resource_policy" "route53-query-logging-policy" { + policy_document = "${data.aws_iam_policy_document.route53-query-logging-policy.json}" + policy_name = "route53-query-logging-policy" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `policy_document` - (Required) Details of the resource policy, including the identity of the principal that is enabled to put logs to this account. This is formatted as a JSON string. Maximum length of 5120 characters. +* `policy_name` - (Required) Name of the resource policy. + +## Attributes Reference + +The following additional attributes are exported: + +* `id` - The name of the CloudWatch log resource policy + +## Import + +CloudWatch log resource policies can be imported using the policy name, e.g. + +``` +$ terraform import aws_cloudwatch_log_resource_policy.MyPolicy MyPolicy +```