diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 095e392dd..66446a902 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -115,6 +115,7 @@ func Provider() terraform.ResourceProvider { "aws_network_interface": resourceAwsNetworkInterface(), "aws_proxy_protocol_policy": resourceAwsProxyProtocolPolicy(), "aws_route53_record": resourceAwsRoute53Record(), + "aws_route53_zone_association": resourceAwsRoute53ZoneAssociation(), "aws_route53_zone": resourceAwsRoute53Zone(), "aws_route_table_association": resourceAwsRouteTableAssociation(), "aws_route_table": resourceAwsRouteTable(), diff --git a/builtin/providers/aws/resource_aws_route53_zone_association.go b/builtin/providers/aws/resource_aws_route53_zone_association.go new file mode 100644 index 000000000..4e1049fcc --- /dev/null +++ b/builtin/providers/aws/resource_aws_route53_zone_association.go @@ -0,0 +1,120 @@ +package aws + +import ( + "log" + + "github.com/hashicorp/terraform/helper/schema" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/route53" +) + +func resourceAwsRoute53ZoneAssociation() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsRoute53ZoneAssociationCreate, + Read: resourceAwsRoute53ZoneAssociationRead, + Update: resourceAwsRoute53ZoneAssociationUpdate, + Delete: resourceAwsRoute53ZoneAssociationDelete, + + Schema: map[string]*schema.Schema{ + "zone_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "region": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "association_id": &schema.Schema{ + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceAwsRoute53ZoneAssociationCreate(d *schema.ResourceData, meta interface{}) error { + r53 := meta.(*AWSClient).r53conn + + req := &route53.AssociateVPCWithHostedZoneInput{ + HostedZoneID: aws.String(d.Get("zone_id").(string)), + VPC: &route53.VPC{ + VPCID: aws.String(d.Get("vpc_id").(string)), + VPCRegion: aws.String(d.Get("region").(string)), + }, + Comment: aws.String("Managed by Terraform"), + } + + log.Printf("[DEBUG] Associating Route53 Private Zone %s with VPC %s", *req.HostedZoneID, *req.VPC.VPCID) + resp, err := r53.AssociateVPCWithHostedZone(req) + if err != nil { + return err + } + + // Store association id + association_id := *resp.ChangeInfo.ID + d.Set("association_id", association_id) + d.SetId(association_id) + + return resourceAwsRoute53ZoneAssociationUpdate(d, meta) +} + +func resourceAwsRoute53ZoneAssociationRead(d *schema.ResourceData, meta interface{}) error { + r53 := meta.(*AWSClient).r53conn + zone, err := r53.GetHostedZone(&route53.GetHostedZoneInput{ID: aws.String(d.Id())}) + if err != nil { + // Handle a deleted zone + if r53err, ok := err.(aws.APIError); ok && r53err.Code == "NoSuchHostedZone" { + d.SetId("") + return nil + } + return err + } + + vpc_id := d.Get("vpc_id") + + for i := range zone.VPCs { + if vpc_id == *zone.VPCs[i].VPCID { + // association is there, return + return nil + } + } + + // no association found + d.SetId("") + return nil +} + +func resourceAwsRoute53ZoneAssociationUpdate(d *schema.ResourceData, meta interface{}) error { + return resourceAwsRoute53ZoneAssociationRead(d, meta) +} + +func resourceAwsRoute53ZoneAssociationDelete(d *schema.ResourceData, meta interface{}) error { + r53 := meta.(*AWSClient).r53conn + + log.Printf("[DEBUG] Deleting Route53 Private Zone (%s) association (ID: %s)", + d.Get("zone_id").(string), d.Id()) + + req := &route53.DisassociateVPCFromHostedZoneInput{ + HostedZoneID: aws.String(d.Get("zone_id").(string)), + VPC: &route53.VPC{ + VPCID: aws.String(d.Get("vpc_id").(string)), + VPCRegion: aws.String(d.Get("region").(string)), + }, + Comment: aws.String("Managed by Terraform"), + } + + _, err := r53.DisassociateVPCFromHostedZone(req) + if err != nil { + return err + } + + return nil +} diff --git a/builtin/providers/aws/resource_aws_route53_zone_association_test.go b/builtin/providers/aws/resource_aws_route53_zone_association_test.go new file mode 100644 index 000000000..f9f1d8ff1 --- /dev/null +++ b/builtin/providers/aws/resource_aws_route53_zone_association_test.go @@ -0,0 +1,101 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + + "github.com/awslabs/aws-sdk-go/aws" + "github.com/awslabs/aws-sdk-go/service/route53" +) + +func TestAccRoute53ZoneAssociation(t *testing.T) { + var zone route53.HostedZone + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckRoute53ZoneAssociationDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccRoute53ZoneAssociationConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckRoute53ZoneAssociationExists("aws_route53_zone_association.main", &zone), + ), + }, + }, + }) +} + +func testAccCheckRoute53ZoneAssociationDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).r53conn + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_route53_zone" { + continue + } + + _, err := conn.GetHostedZone(&route53.GetHostedZoneInput{ID: aws.String(rs.Primary.ID)}) + if err == nil { + return fmt.Errorf("Hosted zone still exists") + } + } + return nil +} + +func testAccCheckRoute53ZoneAssociationExists(n string, zone *route53.HostedZone) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No hosted zone ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).r53conn + resp, err := conn.GetHostedZone(&route53.GetHostedZoneInput{ID: aws.String(rs.Primary.ID)}) + if err != nil { + return fmt.Errorf("Hosted zone err: %v", err) + } + + exists := false + for i := range resp.VPCs { + if rs.Primary.Meta["vpc_id"] == *resp.VPCs[i].VPCID { + exists = true + } + } + if !exists { + return fmt.Errorf("Hosted zone association not found") + } + + *zone = *resp.HostedZone + return nil + } +} + +const testAccRoute53ZoneAssociationConfig = ` +resource "aws_vpc" "mosakos" { + cidr_block = "10.6.0.0/16" + + enable_dns_hostnames = true + enable_dns_support = true +} + +resource "aws_route53_zone" "main" { + name = "mosakos.com" + + tags { + foo = "bar" + Name = "tf-route53-tag-test" + } +} + +resource "aws_route53_zone_association" "main" { + vpc_id = "${aws_vpc.mosakos.id}" + zone_id = "${aws_route53_zone.main.id}" + region = "us-west-2" +} +`