Skip to content

Commit

Permalink
New Resource: aws_route53_hosted_zone_dnssec (#17474)
Browse files Browse the repository at this point in the history
Reference: #16836

Output from acceptance testing in AWS Commercial:

```
--- PASS: TestAccAwsRoute53HostedZoneDnssec_basic (205.85s)
--- PASS: TestAccAwsRoute53HostedZoneDnssec_disappears (232.79s)
--- PASS: TestAccAwsRoute53HostedZoneDnssec_SigningStatus (284.80s)
```

Output from acceptance testing in AWS GovCloud (US):

```
--- SKIP: TestAccAwsRoute53HostedZoneDnssec_basic (2.37s)
--- SKIP: TestAccAwsRoute53HostedZoneDnssec_SigningStatus (2.37s)
--- SKIP: TestAccAwsRoute53HostedZoneDnssec_disappears (2.37s)
```
  • Loading branch information
bflad authored Mar 1, 2021
1 parent 1ea1b33 commit 0bfe068
Show file tree
Hide file tree
Showing 11 changed files with 611 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .changelog/pending.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_route53_hosted_zone_dnssec
```
6 changes: 6 additions & 0 deletions aws/internal/service/route53/enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
14 changes: 14 additions & 0 deletions aws/internal/service/route53/finder/finder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
16 changes: 16 additions & 0 deletions aws/internal/service/route53/waiter/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
34 changes: 34 additions & 0 deletions aws/internal/service/route53/waiter/waiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
const (
ChangeTimeout = 30 * time.Minute

HostedZoneDnssecStatusTimeout = 5 * time.Minute

KeySigningKeyStatusTimeout = 5 * time.Minute
)

Expand All @@ -35,6 +37,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},
Expand Down
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,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(),
Expand Down
210 changes: 210 additions & 0 deletions aws/resource_aws_route53_hosted_zone_dnssec.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 0bfe068

Please sign in to comment.