diff --git a/.changelog/pending.txt b/.changelog/pending.txt new file mode 100644 index 00000000000..86b25700c84 --- /dev/null +++ b/.changelog/pending.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_route53_hosted_zone_dnssec +``` diff --git a/aws/internal/service/route53/enum.go b/aws/internal/service/route53/enum.go index d10f69e9189..bb8204082e5 100644 --- a/aws/internal/service/route53/enum.go +++ b/aws/internal/service/route53/enum.go @@ -6,4 +6,10 @@ const ( KeySigningKeyStatusDeleting = "DELETING" KeySigningKeyStatusInactive = "INACTIVE" KeySigningKeyStatusInternalFailure = "INTERNAL_FAILURE" + + ServeSignatureActionNeeded = "ACTION_NEEDED" + ServeSignatureDeleting = "DELETING" + ServeSignatureInternalFailure = "INTERNAL_FAILURE" + ServeSignatureNotSigning = "NOT_SIGNING" + ServeSignatureSigning = "SIGNING" ) diff --git a/aws/internal/service/route53/finder/finder.go b/aws/internal/service/route53/finder/finder.go index de40116d4e8..7273c85ead6 100644 --- a/aws/internal/service/route53/finder/finder.go +++ b/aws/internal/service/route53/finder/finder.go @@ -8,6 +8,20 @@ import ( tfroute53 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53" ) +func HostedZoneDnssec(conn *route53.Route53, hostedZoneID string) (*route53.GetDNSSECOutput, error) { + input := &route53.GetDNSSECInput{ + HostedZoneId: aws.String(hostedZoneID), + } + + output, err := conn.GetDNSSEC(input) + + if err != nil { + return nil, err + } + + return output, nil +} + func KeySigningKey(conn *route53.Route53, hostedZoneID string, name string) (*route53.KeySigningKey, error) { input := &route53.GetDNSSECInput{ HostedZoneId: aws.String(hostedZoneID), diff --git a/aws/internal/service/route53/waiter/status.go b/aws/internal/service/route53/waiter/status.go index eb520ef2d1e..ac56d38bdaa 100644 --- a/aws/internal/service/route53/waiter/status.go +++ b/aws/internal/service/route53/waiter/status.go @@ -27,6 +27,22 @@ func ChangeInfoStatus(conn *route53.Route53, changeID string) resource.StateRefr } } +func HostedZoneDnssecStatus(conn *route53.Route53, hostedZoneID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + hostedZoneDnssec, err := finder.HostedZoneDnssec(conn, hostedZoneID) + + if err != nil { + return nil, "", err + } + + if hostedZoneDnssec == nil || hostedZoneDnssec.Status == nil { + return nil, "", nil + } + + return hostedZoneDnssec.Status, aws.StringValue(hostedZoneDnssec.Status.ServeSignature), nil + } +} + func KeySigningKeyStatus(conn *route53.Route53, hostedZoneID string, name string) resource.StateRefreshFunc { return func() (interface{}, string, error) { keySigningKey, err := finder.KeySigningKey(conn, hostedZoneID, name) diff --git a/aws/internal/service/route53/waiter/waiter.go b/aws/internal/service/route53/waiter/waiter.go index 0c9647f9ca3..300f4e236aa 100644 --- a/aws/internal/service/route53/waiter/waiter.go +++ b/aws/internal/service/route53/waiter/waiter.go @@ -12,6 +12,8 @@ import ( const ( ChangeTimeout = 30 * time.Minute + HostedZoneDnssecStatusTimeout = 5 * time.Minute + KeySigningKeyStatusTimeout = 5 * time.Minute ) @@ -34,6 +36,38 @@ func ChangeInfoStatusInsync(conn *route53.Route53, changeID string) (*route53.Ch return nil, err } +func HostedZoneDnssecStatusUpdated(conn *route53.Route53, hostedZoneID string, status string) (*route53.DNSSECStatus, error) { + stateConf := &resource.StateChangeConf{ + Target: []string{status}, + Refresh: HostedZoneDnssecStatus(conn, hostedZoneID), + MinTimeout: 5 * time.Second, + Timeout: HostedZoneDnssecStatusTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*route53.DNSSECStatus); ok { + if err != nil && output != nil && output.ServeSignature != nil && output.StatusMessage != nil { + newErr := fmt.Errorf("%s: %s", aws.StringValue(output.ServeSignature), aws.StringValue(output.StatusMessage)) + + switch e := err.(type) { + case *resource.TimeoutError: + if e.LastError == nil { + e.LastError = newErr + } + case *resource.UnexpectedStateError: + if e.LastError == nil { + e.LastError = newErr + } + } + } + + return output, err + } + + return nil, err +} + func KeySigningKeyStatusUpdated(conn *route53.Route53, hostedZoneID string, name string, status string) (*route53.KeySigningKey, error) { stateConf := &resource.StateChangeConf{ Target: []string{status}, diff --git a/aws/provider.go b/aws/provider.go index be2f297a3ad..2536974b8dd 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -867,6 +867,7 @@ func Provider() *schema.Provider { "aws_redshift_event_subscription": resourceAwsRedshiftEventSubscription(), "aws_resourcegroups_group": resourceAwsResourceGroupsGroup(), "aws_route53_delegation_set": resourceAwsRoute53DelegationSet(), + "aws_route53_hosted_zone_dnssec": resourceAwsRoute53HostedZoneDnssec(), "aws_route53_key_signing_key": resourceAwsRoute53KeySigningKey(), "aws_route53_query_log": resourceAwsRoute53QueryLog(), "aws_route53_record": resourceAwsRoute53Record(), diff --git a/aws/resource_aws_route53_hosted_zone_dnssec.go b/aws/resource_aws_route53_hosted_zone_dnssec.go new file mode 100644 index 00000000000..522a66b6eda --- /dev/null +++ b/aws/resource_aws_route53_hosted_zone_dnssec.go @@ -0,0 +1,210 @@ +package aws + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/route53" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + tfroute53 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53/waiter" +) + +func resourceAwsRoute53HostedZoneDnssec() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsRoute53HostedZoneDnssecCreate, + Read: resourceAwsRoute53HostedZoneDnssecRead, + Update: resourceAwsRoute53HostedZoneDnssecUpdate, + Delete: resourceAwsRoute53HostedZoneDnssecDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "hosted_zone_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "signing_status": { + Type: schema.TypeString, + Optional: true, + Default: tfroute53.ServeSignatureSigning, + ValidateFunc: validation.StringInSlice([]string{ + tfroute53.ServeSignatureSigning, + tfroute53.ServeSignatureNotSigning, + }, false), + }, + }, + } +} + +func resourceAwsRoute53HostedZoneDnssecCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).r53conn + + hostedZoneID := d.Get("hosted_zone_id").(string) + signingStatus := d.Get("signing_status").(string) + + d.SetId(hostedZoneID) + + switch signingStatus { + default: + return fmt.Errorf("error updating Route 53 Hosted Zone DNSSEC (%s) signing status: unknown status (%s)", d.Id(), signingStatus) + case tfroute53.ServeSignatureSigning: + if err := route53HostedZoneDnssecEnable(conn, d.Id()); err != nil { + return fmt.Errorf("error enabling Route 53 Hosted Zone DNSSEC (%s): %w", d.Id(), err) + } + case tfroute53.ServeSignatureNotSigning: + if err := route53HostedZoneDnssecDisable(conn, d.Id()); err != nil { + return fmt.Errorf("error disabling Route 53 Hosted Zone DNSSEC (%s): %w", d.Id(), err) + } + } + + if _, err := waiter.HostedZoneDnssecStatusUpdated(conn, d.Id(), signingStatus); err != nil { + return fmt.Errorf("error waiting for Route 53 Hosted Zone DNSSEC (%s) signing status (%s): %w", d.Id(), signingStatus, err) + } + + return resourceAwsRoute53HostedZoneDnssecRead(d, meta) +} + +func resourceAwsRoute53HostedZoneDnssecRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).r53conn + + hostedZoneDnssec, err := finder.HostedZoneDnssec(conn, d.Id()) + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, route53.ErrCodeDNSSECNotFound) { + log.Printf("[WARN] Route 53 Hosted Zone DNSSEC (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, route53.ErrCodeNoSuchHostedZone) { + log.Printf("[WARN] Route 53 Hosted Zone DNSSEC (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading Route 53 Hosted Zone DNSSEC (%s): %w", d.Id(), err) + } + + if hostedZoneDnssec == nil { + if d.IsNewResource() { + return fmt.Errorf("error reading Route 53 Hosted Zone DNSSEC (%s): not found", d.Id()) + } + + log.Printf("[WARN] Route 53 Hosted Zone DNSSEC (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("hosted_zone_id", d.Id()) + + if hostedZoneDnssec.Status != nil { + d.Set("signing_status", hostedZoneDnssec.Status.ServeSignature) + } + + return nil +} + +func resourceAwsRoute53HostedZoneDnssecUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).r53conn + + if d.HasChange("signing_status") { + signingStatus := d.Get("signing_status").(string) + + switch signingStatus { + default: + return fmt.Errorf("error updating Route 53 Hosted Zone DNSSEC (%s) signing status: unknown status (%s)", d.Id(), signingStatus) + case tfroute53.ServeSignatureSigning: + if err := route53HostedZoneDnssecEnable(conn, d.Id()); err != nil { + return fmt.Errorf("error enabling Route 53 Hosted Zone DNSSEC (%s): %w", d.Id(), err) + } + case tfroute53.ServeSignatureNotSigning: + if err := route53HostedZoneDnssecDisable(conn, d.Id()); err != nil { + return fmt.Errorf("error disabling Route 53 Hosted Zone DNSSEC (%s): %w", d.Id(), err) + } + } + + if _, err := waiter.HostedZoneDnssecStatusUpdated(conn, d.Id(), signingStatus); err != nil { + return fmt.Errorf("error waiting for Route 53 Hosted Zone DNSSEC (%s) signing status (%s): %w", d.Id(), signingStatus, err) + } + } + + return resourceAwsRoute53HostedZoneDnssecRead(d, meta) +} + +func resourceAwsRoute53HostedZoneDnssecDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).r53conn + + input := &route53.DisableHostedZoneDNSSECInput{ + HostedZoneId: aws.String(d.Id()), + } + + output, err := conn.DisableHostedZoneDNSSEC(input) + + if tfawserr.ErrCodeEquals(err, route53.ErrCodeDNSSECNotFound) { + return nil + } + + if tfawserr.ErrCodeEquals(err, route53.ErrCodeNoSuchHostedZone) { + return nil + } + + if err != nil { + return fmt.Errorf("error disabling Route 53 Hosted Zone DNSSEC (%s): %w", d.Id(), err) + } + + if output != nil && output.ChangeInfo != nil { + if _, err := waiter.ChangeInfoStatusInsync(conn, aws.StringValue(output.ChangeInfo.Id)); err != nil { + return fmt.Errorf("error waiting for Route 53 Hosted Zone DNSSEC (%s) disable: %w", d.Id(), err) + } + } + + return nil +} + +func route53HostedZoneDnssecDisable(conn *route53.Route53, hostedZoneID string) error { + input := &route53.DisableHostedZoneDNSSECInput{ + HostedZoneId: aws.String(hostedZoneID), + } + + output, err := conn.DisableHostedZoneDNSSEC(input) + + if err != nil { + return fmt.Errorf("error disabling: %w", err) + } + + if output != nil && output.ChangeInfo != nil { + if _, err := waiter.ChangeInfoStatusInsync(conn, aws.StringValue(output.ChangeInfo.Id)); err != nil { + return fmt.Errorf("error waiting for update: %w", err) + } + } + + return nil +} + +func route53HostedZoneDnssecEnable(conn *route53.Route53, hostedZoneID string) error { + input := &route53.EnableHostedZoneDNSSECInput{ + HostedZoneId: aws.String(hostedZoneID), + } + + output, err := conn.EnableHostedZoneDNSSEC(input) + + if err != nil { + return fmt.Errorf("error enabling: %w", err) + } + + if output != nil && output.ChangeInfo != nil { + if _, err := waiter.ChangeInfoStatusInsync(conn, aws.StringValue(output.ChangeInfo.Id)); err != nil { + return fmt.Errorf("error waiting for update: %w", err) + } + } + + return nil +} diff --git a/aws/resource_aws_route53_hosted_zone_dnssec_test.go b/aws/resource_aws_route53_hosted_zone_dnssec_test.go new file mode 100644 index 00000000000..88b51282a68 --- /dev/null +++ b/aws/resource_aws_route53_hosted_zone_dnssec_test.go @@ -0,0 +1,232 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/route53" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + tfroute53 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/route53/finder" +) + +func TestAccAwsRoute53HostedZoneDnssec_basic(t *testing.T) { + route53ZoneResourceName := "aws_route53_zone.test" + resourceName := "aws_route53_hosted_zone_dnssec.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckRoute53KeySigningKey(t) }, + ErrorCheck: testAccErrorCheckSkipRoute53(t), + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAwsRoute53HostedZoneDnssecDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsRoute53HostedZoneDnssecConfig(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccAwsRoute53HostedZoneDnssecExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "hosted_zone_id", route53ZoneResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "signing_status", tfroute53.ServeSignatureSigning), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsRoute53HostedZoneDnssec_disappears(t *testing.T) { + resourceName := "aws_route53_hosted_zone_dnssec.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckRoute53KeySigningKey(t) }, + ErrorCheck: testAccErrorCheckSkipRoute53(t), + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAwsRoute53HostedZoneDnssecDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsRoute53HostedZoneDnssecConfig(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccAwsRoute53HostedZoneDnssecExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsRoute53HostedZoneDnssec(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAwsRoute53HostedZoneDnssec_SigningStatus(t *testing.T) { + resourceName := "aws_route53_hosted_zone_dnssec.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckRoute53KeySigningKey(t) }, + ErrorCheck: testAccErrorCheckSkipRoute53(t), + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAwsRoute53HostedZoneDnssecDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsRoute53HostedZoneDnssecConfig_SigningStatus(rName, tfroute53.ServeSignatureNotSigning), + Check: resource.ComposeAggregateTestCheckFunc( + testAccAwsRoute53HostedZoneDnssecExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "signing_status", tfroute53.ServeSignatureNotSigning), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAwsRoute53HostedZoneDnssecConfig_SigningStatus(rName, tfroute53.ServeSignatureSigning), + Check: resource.ComposeAggregateTestCheckFunc( + testAccAwsRoute53HostedZoneDnssecExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "signing_status", tfroute53.ServeSignatureSigning), + ), + }, + { + Config: testAccAwsRoute53HostedZoneDnssecConfig_SigningStatus(rName, tfroute53.ServeSignatureNotSigning), + Check: resource.ComposeAggregateTestCheckFunc( + testAccAwsRoute53HostedZoneDnssecExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "signing_status", tfroute53.ServeSignatureNotSigning), + ), + }, + }, + }) +} + +func testAccCheckAwsRoute53HostedZoneDnssecDestroy(s *terraform.State) error { + conn := testAccProviderRoute53KeySigningKey.Meta().(*AWSClient).r53conn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_route53_hosted_zone_dnssec" { + continue + } + + hostedZoneDnssec, err := finder.HostedZoneDnssec(conn, rs.Primary.ID) + + if tfawserr.ErrCodeEquals(err, route53.ErrCodeDNSSECNotFound) { + continue + } + + if tfawserr.ErrCodeEquals(err, route53.ErrCodeNoSuchHostedZone) { + continue + } + + if err != nil { + return fmt.Errorf("error reading Route 53 Hosted Zone DNSSEC (%s): %w", rs.Primary.ID, err) + } + + if hostedZoneDnssec != nil && hostedZoneDnssec.Status != nil && aws.StringValue(hostedZoneDnssec.Status.ServeSignature) == tfroute53.ServeSignatureSigning { + return fmt.Errorf("Route 53 Hosted Zone DNSSEC (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccAwsRoute53HostedZoneDnssecExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + + if !ok { + return fmt.Errorf("resource %s not found", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("resource %s has not set its id", resourceName) + } + + conn := testAccProviderRoute53KeySigningKey.Meta().(*AWSClient).r53conn + + hostedZoneDnssec, err := finder.HostedZoneDnssec(conn, rs.Primary.ID) + + if err != nil { + return fmt.Errorf("error reading Route 53 Hosted Zone DNSSEC (%s): %w", rs.Primary.ID, err) + } + + if hostedZoneDnssec == nil { + return fmt.Errorf("Route 53 Hosted Zone DNSSEC (%s) not found", rs.Primary.ID) + } + + return nil + } +} + +func testAccAwsRoute53HostedZoneDnssecConfig_Base(rName string) string { + return composeConfig( + testAccRoute53KeySigningKeyRegionProviderConfig(), + fmt.Sprintf(` +resource "aws_kms_key" "test" { + customer_master_key_spec = "ECC_NIST_P256" + deletion_window_in_days = 7 + key_usage = "SIGN_VERIFY" + policy = jsonencode({ + Statement = [ + { + Action = [ + "kms:DescribeKey", + "kms:GetPublicKey", + "kms:Sign", + ], + Effect = "Allow" + Principal = { + Service = "api-service.dnssec.route53.aws.internal" + } + Sid = "Allow Route 53 DNSSEC Service" + }, + { + Action = "kms:*" + Effect = "Allow" + Principal = { + AWS = "*" + } + Resource = "*" + Sid = "Enable IAM User Permissions" + }, + ] + Version = "2012-10-17" + }) +} + +resource "aws_route53_zone" "test" { + name = "%[1]s.terraformtest.com" +} + +resource "aws_route53_key_signing_key" "test" { + hosted_zone_id = aws_route53_zone.test.id + key_management_service_arn = aws_kms_key.test.arn + name = %[1]q +} +`, rName)) +} + +func testAccAwsRoute53HostedZoneDnssecConfig(rName string) string { + return composeConfig( + testAccAwsRoute53HostedZoneDnssecConfig_Base(rName), + ` +resource "aws_route53_hosted_zone_dnssec" "test" { + hosted_zone_id = aws_route53_key_signing_key.test.hosted_zone_id +} +`) +} + +func testAccAwsRoute53HostedZoneDnssecConfig_SigningStatus(rName string, signingStatus string) string { + return composeConfig( + testAccAwsRoute53HostedZoneDnssecConfig_Base(rName), + fmt.Sprintf(` +resource "aws_route53_hosted_zone_dnssec" "test" { + hosted_zone_id = aws_route53_key_signing_key.test.hosted_zone_id + signing_status = %[1]q +} +`, signingStatus)) +} diff --git a/website/docs/r/route53_hosted_zone_dnssec.html.markdown b/website/docs/r/route53_hosted_zone_dnssec.html.markdown new file mode 100644 index 00000000000..ae35fca4b0f --- /dev/null +++ b/website/docs/r/route53_hosted_zone_dnssec.html.markdown @@ -0,0 +1,89 @@ +--- +subcategory: "Route53" +layout: "aws" +page_title: "AWS: aws_route53_hosted_zone_dnssec" +description: |- + Manages Route 53 Hosted Zone DNSSEC +--- + +# Resource: aws_route53_hosted_zone_dnssec + +Manages Route 53 Hosted Zone Domain Name System Security Extensions (DNSSEC). For more information about managing DNSSEC in Route 53, see the [Route 53 Developer Guide](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-configuring-dnssec.html). + +## Example Usage + +```hcl +provider "aws" { + region = "us-east-1" +} + +resource "aws_kms_key" "example" { + customer_master_key_spec = "ECC_NIST_P256" + deletion_window_in_days = 7 + key_usage = "SIGN_VERIFY" + policy = jsonencode({ + Statement = [ + { + Action = [ + "kms:DescribeKey", + "kms:GetPublicKey", + "kms:Sign", + ], + Effect = "Allow" + Principal = { + Service = "api-service.dnssec.route53.aws.internal" + } + Sid = "Route 53 DNSSEC Permissions" + }, + { + Action = "kms:*" + Effect = "Allow" + Principal = { + AWS = "*" + } + Resource = "*" + Sid = "IAM User Permissions" + }, + ] + Version = "2012-10-17" + }) +} + +resource "aws_route53_zone" "example" { + name = "example.com" +} + +resource "aws_route53_key_signing_key" "example" { + hosted_zone_id = aws_route53_zone.test.id + key_management_service_arn = aws_kms_key.test.arn + name = "example" +} + +resource "aws_route53_hosted_zone_dnssec" "example" { + hosted_zone_id = aws_route53_key_signing_key.example.hosted_zone_id +} +``` + +## Argument Reference + +The following arguments are required: + +* `hosted_zone_id` - (Required) Identifier of the Route 53 Hosted Zone. + +The following arguments are optional: + +* `signing_status` - (Optional) Hosted Zone signing status. Valid values: `SIGNING`, `NOT_SIGNING`. Defaults to `SIGNING`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - Route 53 Hosted Zone identifier. + +## Import + +`aws_route53_hosted_zone_dnssec` resources can be imported by using the Route 53 Hosted Zone identifier, e.g. + +``` +$ terraform import aws_route53_hosted_zone_dnssec.example Z1D633PJN98FT9 +``` diff --git a/website/docs/r/route53_key_signing_key.html.markdown b/website/docs/r/route53_key_signing_key.html.markdown index c0aa9d65356..c61c74063c4 100644 --- a/website/docs/r/route53_key_signing_key.html.markdown +++ b/website/docs/r/route53_key_signing_key.html.markdown @@ -8,7 +8,7 @@ description: |- # Resource: aws_route53_key_signing_key -Manages an Route 53 Key Signing Key. For more information about managing Domain Name System Security Extensions (DNSSEC)in Route 53, see the [Route 53 Developer Guide](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-configuring-dnssec.html). +Manages a Route 53 Key Signing Key. To manage Domain Name System Security Extensions (DNSSEC) for a Hosted Zone, see the [`aws_route53_hosted_zone_dnssec` resource](route53_hosted_zone_dnssec.html). For more information about managing DNSSEC in Route 53, see the [Route 53 Developer Guide](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-configuring-dnssec.html). ## Example Usage @@ -58,6 +58,10 @@ resource "aws_route53_key_signing_key" "example" { key_management_service_arn = aws_kms_key.test.arn name = "example" } + +resource "aws_route53_hosted_zone_dnssec" "example" { + hosted_zone_id = aws_route53_key_signing_key.example.hosted_zone_id +} ``` ## Argument Reference diff --git a/website/docs/r/route53_zone.html.markdown b/website/docs/r/route53_zone.html.markdown index 56a7e1a5cfd..1b1e2690cf6 100644 --- a/website/docs/r/route53_zone.html.markdown +++ b/website/docs/r/route53_zone.html.markdown @@ -8,7 +8,7 @@ description: |- # Resource: aws_route53_zone -Manages a Route53 Hosted Zone. +Manages a Route53 Hosted Zone. For managing Domain Name System Security Extensions (DNSSEC), see the [`aws_route53_key_signing_key`](route53_key_signing_key.html) and [`aws_route53_hosted_zone_dnssec`](route53_hosted_zone_dnssec.html) resources. ## Example Usage