From 2eba2869f943b4a1c5e3b37f074c93d4d94c4ad9 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Tue, 26 Dec 2017 15:05:01 -0500 Subject: [PATCH] New Resource: aws_route53_query_log --- aws/provider.go | 1 + aws/resource_aws_route53_query_log.go | 91 ++++++++++ aws/resource_aws_route53_query_log_test.go | 164 ++++++++++++++++++ website/aws.erb | 4 + .../docs/r/route53_query_log.html.markdown | 93 ++++++++++ 5 files changed, 353 insertions(+) create mode 100644 aws/resource_aws_route53_query_log.go create mode 100644 aws/resource_aws_route53_query_log_test.go create mode 100644 website/docs/r/route53_query_log.html.markdown diff --git a/aws/provider.go b/aws/provider.go index 8fb671307d5..bb511d39827 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -438,6 +438,7 @@ func Provider() terraform.ResourceProvider { "aws_redshift_parameter_group": resourceAwsRedshiftParameterGroup(), "aws_redshift_subnet_group": resourceAwsRedshiftSubnetGroup(), "aws_route53_delegation_set": resourceAwsRoute53DelegationSet(), + "aws_route53_query_log": resourceAwsRoute53QueryLog(), "aws_route53_record": resourceAwsRoute53Record(), "aws_route53_zone_association": resourceAwsRoute53ZoneAssociation(), "aws_route53_zone": resourceAwsRoute53Zone(), diff --git a/aws/resource_aws_route53_query_log.go b/aws/resource_aws_route53_query_log.go new file mode 100644 index 00000000000..f992141f205 --- /dev/null +++ b/aws/resource_aws_route53_query_log.go @@ -0,0 +1,91 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/schema" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/route53" +) + +func resourceAwsRoute53QueryLog() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsRoute53QueryLogCreate, + Read: resourceAwsRoute53QueryLogRead, + Delete: resourceAwsRoute53QueryLogDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "cloudwatch_log_group_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + + "zone_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceAwsRoute53QueryLogCreate(d *schema.ResourceData, meta interface{}) error { + r53 := meta.(*AWSClient).r53conn + + input := &route53.CreateQueryLoggingConfigInput{ + CloudWatchLogsLogGroupArn: aws.String(d.Get("cloudwatch_log_group_arn").(string)), + HostedZoneId: aws.String(d.Get("zone_id").(string)), + } + + log.Printf("[DEBUG] Creating Route53 query logging configuration: %#v", input) + out, err := r53.CreateQueryLoggingConfig(input) + if err != nil { + return fmt.Errorf("Error creating Route53 query logging configuration: %s", err) + } + log.Printf("[DEBUG] Route53 query logging configuration created: %#v", out) + + d.SetId(*out.QueryLoggingConfig.Id) + + return resourceAwsRoute53QueryLogRead(d, meta) +} + +func resourceAwsRoute53QueryLogRead(d *schema.ResourceData, meta interface{}) error { + r53 := meta.(*AWSClient).r53conn + + input := &route53.GetQueryLoggingConfigInput{ + Id: aws.String(d.Id()), + } + log.Printf("[DEBUG] Reading Route53 query logging configuration: %#v", input) + out, err := r53.GetQueryLoggingConfig(input) + if err != nil { + return fmt.Errorf("Error reading Route53 query logging configuration: %s", err) + } + log.Printf("[DEBUG] Route53 query logging configuration received: %#v", out) + + d.Set("cloudwatch_log_group_arn", out.QueryLoggingConfig.CloudWatchLogsLogGroupArn) + d.Set("zone_id", out.QueryLoggingConfig.HostedZoneId) + + return nil +} + +func resourceAwsRoute53QueryLogDelete(d *schema.ResourceData, meta interface{}) error { + r53 := meta.(*AWSClient).r53conn + + input := &route53.DeleteQueryLoggingConfigInput{ + Id: aws.String(d.Id()), + } + log.Printf("[DEBUG] Deleting Route53 query logging configuration: %#v", input) + _, err := r53.DeleteQueryLoggingConfig(input) + if err != nil { + return fmt.Errorf("Error deleting Route53 query logging configuration: %s", err) + } + + return nil +} diff --git a/aws/resource_aws_route53_query_log_test.go b/aws/resource_aws_route53_query_log_test.go new file mode 100644 index 00000000000..411476b4812 --- /dev/null +++ b/aws/resource_aws_route53_query_log_test.go @@ -0,0 +1,164 @@ +package aws + +import ( + "fmt" + "os" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/route53" + + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSRoute53QueryLog_Basic(t *testing.T) { + // The underlying resources are sensitive to where they are located + // Use us-east-1 for testing + oldRegion := os.Getenv("AWS_DEFAULT_REGION") + os.Setenv("AWS_DEFAULT_REGION", "us-east-1") + defer os.Setenv("AWS_DEFAULT_REGION", oldRegion) + + resourceName := "aws_route53_query_log.test" + rName := fmt.Sprintf("%s-%s", t.Name(), acctest.RandString(5)) + + var queryLoggingConfig route53.QueryLoggingConfig + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRoute53QueryLogDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckAWSRoute53QueryLogResourceConfigBasic1(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckRoute53QueryLogExists(resourceName, &queryLoggingConfig), + resource.TestMatchResourceAttr(resourceName, "cloudwatch_log_group_arn", + regexp.MustCompile(fmt.Sprintf(`^arn:aws:logs:[^:]+:[0-9]{12}:log-group:/aws/route53/%s.com:\*$`, rName))), + resource.TestCheckResourceAttrSet(resourceName, "zone_id"), + ), + }, + }, + }) +} + +func TestAccAWSRoute53QueryLog_Import(t *testing.T) { + // The underlying resources are sensitive to where they are located + // Use us-east-1 for testing + oldRegion := os.Getenv("AWS_DEFAULT_REGION") + os.Setenv("AWS_DEFAULT_REGION", "us-east-1") + defer os.Setenv("AWS_DEFAULT_REGION", oldRegion) + + resourceName := "aws_route53_query_log.test" + rName := fmt.Sprintf("%s-%s", t.Name(), acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRoute53QueryLogDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckAWSRoute53QueryLogResourceConfigBasic1(rName), + }, + + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckRoute53QueryLogExists(pr string, queryLoggingConfig *route53.QueryLoggingConfig) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).r53conn + 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") + } + + out, err := conn.GetQueryLoggingConfig(&route53.GetQueryLoggingConfigInput{ + Id: aws.String(rs.Primary.ID), + }) + if err != nil { + return err + } + if out.QueryLoggingConfig == nil { + return fmt.Errorf("Route53 query logging configuration does not exist: %q", rs.Primary.ID) + } + + *queryLoggingConfig = *out.QueryLoggingConfig + + return nil + } +} + +func testAccCheckRoute53QueryLogDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).r53conn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_route53_query_log" { + continue + } + + out, err := conn.GetQueryLoggingConfig(&route53.GetQueryLoggingConfigInput{ + Id: aws.String(rs.Primary.ID), + }) + if err != nil { + return nil + } + + if out.QueryLoggingConfig != nil { + return fmt.Errorf("Route53 query logging configuration exists: %q", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAWSRoute53QueryLogResourceConfigBasic1(rName string) string { + return fmt.Sprintf(` +resource "aws_cloudwatch_log_group" "test" { + name = "/aws/route53/${aws_route53_zone.test.name}" + retention_in_days = 1 +} + +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 = "%[1]s" + policy_document = "${data.aws_iam_policy_document.test.json}" +} + +resource "aws_route53_zone" "test" { + name = "%[1]s.com" +} + +resource "aws_route53_query_log" "test" { + depends_on = ["aws_cloudwatch_log_resource_policy.test"] + + cloudwatch_log_group_arn = "${aws_cloudwatch_log_group.test.arn}" + zone_id = "${aws_route53_zone.test.zone_id}" +} +`, rName) +} diff --git a/website/aws.erb b/website/aws.erb index 5bf9ba7af53..7b64f20da87 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -1357,6 +1357,10 @@ aws_route53_health_check + > + aws_route53_query_log + + > aws_route53_record diff --git a/website/docs/r/route53_query_log.html.markdown b/website/docs/r/route53_query_log.html.markdown new file mode 100644 index 00000000000..e3704b149ef --- /dev/null +++ b/website/docs/r/route53_query_log.html.markdown @@ -0,0 +1,93 @@ +--- +layout: "aws" +page_title: "AWS: aws_route53_query_log" +sidebar_current: "docs-aws-resource-route53-query-log" +description: |- + Provides a Route53 query logging configuration resource. +--- + +# aws_route53_query_log + +Provides a Route53 query logging configuration resource. + +~> **NOTE:** There are restrictions on the configuration of query logging. Notably, +the CloudWatch log group must be in the `us-east-1` region, +a permissive CloudWatch log resource policy must be in place, and +the Route53 hosted zone must be public. +See [Configuring Logging for DNS Queries](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/query-logs.html?console_help=true#query-logs-configuring) for additional details. + +## Example Usage + +```hcl +# Example CloudWatch log group in us-east-1 + +provider "aws" { + alias = "us-east-1" + region = "us-east-1" +} + +resource "aws_cloudwatch_log_group" "aws_route53_example_com" { + provider = "aws.us-east-1" + + name = "/aws/route53/${aws_route53_zone.example_com.name}" + retention_in_days = 30 +} + +# Example CloudWatch log resource policy to allow Route53 to write logs +# to any log group under /aws/route53/* + +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" +} + +# Example Route53 zone with query logging + +resource "aws_route53_zone" "example_com" { + name = "example.com" +} + +resource "aws_route53_query_log" "example_com" { + depends_on = ["aws_cloudwatch_log_resource_policy.route53-query-logging-policy"] + + cloudwatch_log_group_arn = "${aws_cloudwatch_log_group.aws_route53_example_com.arn}" + zone_id = "${aws_route53_zone.example_com.zone_id}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `cloudwatch_log_group_arn` - (Required) CloudWatch log group ARN to send query logs. +* `zone_id` - (Required) Route53 hosted zone ID to enable query logs. + +## Attributes Reference + +The following additional attributes are exported: + +* `id` - The query logging configuration ID + +## Import + +Route53 query logging configurations can be imported using their ID, e.g. + +``` +$ terraform import aws_route53_query_log.example_com xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +```