Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Resource: aws_route53_query_log #2770

Merged
merged 1 commit into from
Jan 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
91 changes: 91 additions & 0 deletions aws/resource_aws_route53_query_log.go
Original file line number Diff line number Diff line change
@@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick, but all AWS SDK structs have reasonable String() implementation, so it's safe (and usually better) to just use %s.


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
}
164 changes: 164 additions & 0 deletions aws/resource_aws_route53_query_log_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
4 changes: 4 additions & 0 deletions website/aws.erb
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,10 @@
<a href="/docs/providers/aws/r/route53_health_check.html">aws_route53_health_check</a>
</li>

<li<%= sidebar_current("docs-aws-resource-route53-query-log") %>>
<a href="/docs/providers/aws/r/route53_query_log.html">aws_route53_query_log</a>
</li>

<li<%= sidebar_current("docs-aws-resource-route53-record") %>>
<a href="/docs/providers/aws/r/route53_record.html">aws_route53_record</a>
</li>
Expand Down
93 changes: 93 additions & 0 deletions website/docs/r/route53_query_log.html.markdown
Original file line number Diff line number Diff line change
@@ -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
```