2014-07-22 23:55:19 +02:00
|
|
|
package aws
|
|
|
|
|
|
|
|
import (
|
2015-04-14 21:54:38 +02:00
|
|
|
"fmt"
|
2014-07-22 23:55:19 +02:00
|
|
|
"log"
|
2015-04-23 15:48:19 +02:00
|
|
|
"sort"
|
2014-07-27 02:56:04 +02:00
|
|
|
"strings"
|
2014-07-22 23:55:19 +02:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/helper/resource"
|
2014-11-21 17:58:34 +01:00
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
2015-02-12 17:48:48 +01:00
|
|
|
|
2015-06-03 20:36:57 +02:00
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
|
|
"github.com/aws/aws-sdk-go/service/route53"
|
2014-07-22 23:55:19 +02:00
|
|
|
)
|
|
|
|
|
2014-11-21 17:58:34 +01:00
|
|
|
func resourceAwsRoute53Zone() *schema.Resource {
|
|
|
|
return &schema.Resource{
|
|
|
|
Create: resourceAwsRoute53ZoneCreate,
|
|
|
|
Read: resourceAwsRoute53ZoneRead,
|
2015-03-26 22:45:23 +01:00
|
|
|
Update: resourceAwsRoute53ZoneUpdate,
|
2014-11-21 17:58:34 +01:00
|
|
|
Delete: resourceAwsRoute53ZoneDelete,
|
|
|
|
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"name": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
2015-05-21 22:02:28 +02:00
|
|
|
"comment": &schema.Schema{
|
2015-04-13 19:42:20 +02:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
2015-05-31 16:05:41 +02:00
|
|
|
Default: "Managed by Terraform",
|
2015-04-13 19:42:20 +02:00
|
|
|
},
|
|
|
|
|
2015-05-21 22:02:28 +02:00
|
|
|
"vpc_id": &schema.Schema{
|
2015-05-21 21:51:53 +02:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
2015-05-21 22:02:28 +02:00
|
|
|
ForceNew: true,
|
2015-05-21 21:51:53 +02:00
|
|
|
},
|
|
|
|
|
2015-04-13 19:42:20 +02:00
|
|
|
"vpc_region": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
2015-05-08 21:47:42 +02:00
|
|
|
Computed: true,
|
2015-04-13 19:42:20 +02:00
|
|
|
},
|
|
|
|
|
2014-11-21 17:58:34 +01:00
|
|
|
"zone_id": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
2015-03-26 22:45:23 +01:00
|
|
|
|
2015-05-31 16:05:41 +02:00
|
|
|
"delegation_set_id": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
2015-04-15 23:07:50 +02:00
|
|
|
"name_servers": &schema.Schema{
|
2015-04-22 20:27:20 +02:00
|
|
|
Type: schema.TypeList,
|
2015-04-14 21:17:01 +02:00
|
|
|
Elem: &schema.Schema{Type: schema.TypeString},
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
|
2015-03-26 22:45:23 +01:00
|
|
|
"tags": tagsSchema(),
|
2014-07-22 23:55:19 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-21 17:58:34 +01:00
|
|
|
func resourceAwsRoute53ZoneCreate(d *schema.ResourceData, meta interface{}) error {
|
2015-02-12 17:48:48 +01:00
|
|
|
r53 := meta.(*AWSClient).r53conn
|
2014-07-22 23:55:19 +02:00
|
|
|
|
2015-04-16 20:42:16 +02:00
|
|
|
req := &route53.CreateHostedZoneInput{
|
2015-02-12 17:48:48 +01:00
|
|
|
Name: aws.String(d.Get("name").(string)),
|
2015-05-21 23:23:29 +02:00
|
|
|
HostedZoneConfig: &route53.HostedZoneConfig{Comment: aws.String(d.Get("comment").(string))},
|
2015-02-12 17:48:48 +01:00
|
|
|
CallerReference: aws.String(time.Now().Format(time.RFC3339Nano)),
|
2014-07-22 23:55:19 +02:00
|
|
|
}
|
2015-05-08 17:06:27 +02:00
|
|
|
if v := d.Get("vpc_id"); v != "" {
|
2015-04-13 19:42:20 +02:00
|
|
|
req.VPC = &route53.VPC{
|
2015-08-17 20:27:16 +02:00
|
|
|
VPCId: aws.String(v.(string)),
|
2015-04-13 19:42:20 +02:00
|
|
|
VPCRegion: aws.String(meta.(*AWSClient).region),
|
|
|
|
}
|
2015-05-08 15:58:35 +02:00
|
|
|
if w := d.Get("vpc_region"); w != "" {
|
2015-04-13 19:42:20 +02:00
|
|
|
req.VPC.VPCRegion = aws.String(w.(string))
|
|
|
|
}
|
2015-05-11 16:47:08 +02:00
|
|
|
d.Set("vpc_region", req.VPC.VPCRegion)
|
2015-04-13 19:42:20 +02:00
|
|
|
}
|
2015-02-12 17:48:48 +01:00
|
|
|
|
2015-05-31 16:05:41 +02:00
|
|
|
if v, ok := d.GetOk("delegation_set_id"); ok {
|
2015-08-17 20:27:16 +02:00
|
|
|
req.DelegationSetId = aws.String(v.(string))
|
2015-05-31 16:05:41 +02:00
|
|
|
}
|
|
|
|
|
2015-02-12 17:48:48 +01:00
|
|
|
log.Printf("[DEBUG] Creating Route53 hosted zone: %s", *req.Name)
|
2015-05-20 13:21:23 +02:00
|
|
|
var err error
|
2014-07-22 23:55:19 +02:00
|
|
|
resp, err := r53.CreateHostedZone(req)
|
|
|
|
if err != nil {
|
2014-11-21 17:58:34 +01:00
|
|
|
return err
|
2014-07-22 23:55:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Store the zone_id
|
2015-08-17 20:27:16 +02:00
|
|
|
zone := cleanZoneID(*resp.HostedZone.Id)
|
2014-11-21 17:58:34 +01:00
|
|
|
d.Set("zone_id", zone)
|
|
|
|
d.SetId(zone)
|
2014-07-22 23:55:19 +02:00
|
|
|
|
|
|
|
// Wait until we are done initializing
|
|
|
|
wait := resource.StateChangeConf{
|
2014-07-23 05:08:39 +02:00
|
|
|
Delay: 30 * time.Second,
|
2014-07-23 00:44:59 +02:00
|
|
|
Pending: []string{"PENDING"},
|
2016-01-21 02:20:41 +01:00
|
|
|
Target: []string{"INSYNC"},
|
2014-07-23 00:44:59 +02:00
|
|
|
Timeout: 10 * time.Minute,
|
2014-11-13 16:52:10 +01:00
|
|
|
MinTimeout: 2 * time.Second,
|
2014-07-22 23:55:19 +02:00
|
|
|
Refresh: func() (result interface{}, state string, err error) {
|
2015-04-16 20:42:16 +02:00
|
|
|
changeRequest := &route53.GetChangeInput{
|
2015-08-17 20:27:16 +02:00
|
|
|
Id: aws.String(cleanChangeID(*resp.ChangeInfo.Id)),
|
2015-02-12 17:48:48 +01:00
|
|
|
}
|
|
|
|
return resourceAwsGoRoute53Wait(r53, changeRequest)
|
2014-07-22 23:55:19 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
_, err = wait.WaitForState()
|
|
|
|
if err != nil {
|
2014-11-21 17:58:34 +01:00
|
|
|
return err
|
2014-07-22 23:55:19 +02:00
|
|
|
}
|
2015-03-26 22:45:23 +01:00
|
|
|
return resourceAwsRoute53ZoneUpdate(d, meta)
|
2014-07-22 23:55:19 +02:00
|
|
|
}
|
|
|
|
|
2014-11-21 17:58:34 +01:00
|
|
|
func resourceAwsRoute53ZoneRead(d *schema.ResourceData, meta interface{}) error {
|
2015-02-12 17:48:48 +01:00
|
|
|
r53 := meta.(*AWSClient).r53conn
|
2015-08-17 20:27:16 +02:00
|
|
|
zone, err := r53.GetHostedZone(&route53.GetHostedZoneInput{Id: aws.String(d.Id())})
|
2014-07-22 23:55:19 +02:00
|
|
|
if err != nil {
|
2014-11-21 17:58:34 +01:00
|
|
|
// Handle a deleted zone
|
2015-05-20 13:21:23 +02:00
|
|
|
if r53err, ok := err.(awserr.Error); ok && r53err.Code() == "NoSuchHostedZone" {
|
2014-11-21 17:58:34 +01:00
|
|
|
d.SetId("")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return err
|
2014-07-22 23:55:19 +02:00
|
|
|
}
|
2014-11-21 17:58:34 +01:00
|
|
|
|
2015-05-09 05:21:22 +02:00
|
|
|
if !*zone.HostedZone.Config.PrivateZone {
|
2015-05-08 15:58:35 +02:00
|
|
|
ns := make([]string, len(zone.DelegationSet.NameServers))
|
|
|
|
for i := range zone.DelegationSet.NameServers {
|
|
|
|
ns[i] = *zone.DelegationSet.NameServers[i]
|
|
|
|
}
|
|
|
|
sort.Strings(ns)
|
|
|
|
if err := d.Set("name_servers", ns); err != nil {
|
|
|
|
return fmt.Errorf("[DEBUG] Error setting name servers for: %s, error: %#v", d.Id(), err)
|
|
|
|
}
|
|
|
|
} else {
|
2015-05-11 17:20:34 +02:00
|
|
|
ns, err := getNameServers(d.Id(), d.Get("name").(string), r53)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := d.Set("name_servers", ns); err != nil {
|
|
|
|
return fmt.Errorf("[DEBUG] Error setting name servers for: %s, error: %#v", d.Id(), err)
|
|
|
|
}
|
|
|
|
|
2015-05-08 17:06:27 +02:00
|
|
|
var associatedVPC *route53.VPC
|
|
|
|
for _, vpc := range zone.VPCs {
|
2015-08-17 20:27:16 +02:00
|
|
|
if *vpc.VPCId == d.Get("vpc_id") {
|
2015-05-08 17:06:27 +02:00
|
|
|
associatedVPC = vpc
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if associatedVPC == nil {
|
|
|
|
return fmt.Errorf("[DEBUG] VPC: %v is not associated with Zone: %v", d.Get("vpc_id"), d.Id())
|
|
|
|
}
|
2015-04-14 21:54:38 +02:00
|
|
|
}
|
2015-04-14 21:17:01 +02:00
|
|
|
|
2015-08-17 20:27:16 +02:00
|
|
|
if zone.DelegationSet != nil && zone.DelegationSet.Id != nil {
|
|
|
|
d.Set("delegation_set_id", cleanDelegationSetId(*zone.DelegationSet.Id))
|
2015-05-31 16:05:41 +02:00
|
|
|
}
|
|
|
|
|
2015-03-26 22:45:23 +01:00
|
|
|
// get tags
|
2015-04-16 20:42:16 +02:00
|
|
|
req := &route53.ListTagsForResourceInput{
|
2015-08-17 20:27:16 +02:00
|
|
|
ResourceId: aws.String(d.Id()),
|
2015-03-26 22:45:23 +01:00
|
|
|
ResourceType: aws.String("hostedzone"),
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := r53.ListTagsForResource(req)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-04-16 20:42:16 +02:00
|
|
|
var tags []*route53.Tag
|
2015-03-26 22:45:23 +01:00
|
|
|
if resp.ResourceTagSet != nil {
|
|
|
|
tags = resp.ResourceTagSet.Tags
|
|
|
|
}
|
2015-04-01 21:49:50 +02:00
|
|
|
|
|
|
|
if err := d.Set("tags", tagsToMapR53(tags)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-03-26 22:45:23 +01:00
|
|
|
|
2014-11-21 17:58:34 +01:00
|
|
|
return nil
|
2014-07-22 23:55:19 +02:00
|
|
|
}
|
|
|
|
|
2015-03-26 22:45:23 +01:00
|
|
|
func resourceAwsRoute53ZoneUpdate(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
conn := meta.(*AWSClient).r53conn
|
|
|
|
|
2015-04-28 18:11:38 +02:00
|
|
|
if err := setTagsR53(conn, d, "hostedzone"); err != nil {
|
2015-03-26 22:45:23 +01:00
|
|
|
return err
|
|
|
|
} else {
|
|
|
|
d.SetPartial("tags")
|
|
|
|
}
|
|
|
|
|
|
|
|
return resourceAwsRoute53ZoneRead(d, meta)
|
|
|
|
}
|
|
|
|
|
2014-11-21 17:58:34 +01:00
|
|
|
func resourceAwsRoute53ZoneDelete(d *schema.ResourceData, meta interface{}) error {
|
2015-02-12 17:48:48 +01:00
|
|
|
r53 := meta.(*AWSClient).r53conn
|
2014-07-22 23:55:19 +02:00
|
|
|
|
|
|
|
log.Printf("[DEBUG] Deleting Route53 hosted zone: %s (ID: %s)",
|
2014-11-21 17:58:34 +01:00
|
|
|
d.Get("name").(string), d.Id())
|
2015-08-17 20:27:16 +02:00
|
|
|
_, err := r53.DeleteHostedZone(&route53.DeleteHostedZoneInput{Id: aws.String(d.Id())})
|
2014-07-22 23:55:19 +02:00
|
|
|
if err != nil {
|
2015-12-07 23:33:37 +01:00
|
|
|
if r53err, ok := err.(awserr.Error); ok && r53err.Code() == "NoSuchHostedZone" {
|
|
|
|
log.Printf("[DEBUG] No matching Route 53 Zone found for: %s, removing from state file", d.Id())
|
|
|
|
d.SetId("")
|
|
|
|
return nil
|
|
|
|
}
|
2014-07-22 23:55:19 +02:00
|
|
|
return err
|
|
|
|
}
|
2014-11-21 17:58:34 +01:00
|
|
|
|
2014-07-22 23:55:19 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-04-14 16:51:23 +02:00
|
|
|
func resourceAwsGoRoute53Wait(r53 *route53.Route53, ref *route53.GetChangeInput) (result interface{}, state string, err error) {
|
2015-02-12 17:48:48 +01:00
|
|
|
|
2014-11-21 17:58:34 +01:00
|
|
|
status, err := r53.GetChange(ref)
|
2014-07-22 23:55:19 +02:00
|
|
|
if err != nil {
|
2014-11-21 17:58:34 +01:00
|
|
|
return nil, "UNKNOWN", err
|
2014-07-22 23:55:19 +02:00
|
|
|
}
|
2015-02-12 17:48:48 +01:00
|
|
|
return true, *status.ChangeInfo.Status, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// cleanChangeID is used to remove the leading /change/
|
|
|
|
func cleanChangeID(ID string) string {
|
|
|
|
return cleanPrefix(ID, "/change/")
|
|
|
|
}
|
|
|
|
|
|
|
|
// cleanZoneID is used to remove the leading /hostedzone/
|
|
|
|
func cleanZoneID(ID string) string {
|
|
|
|
return cleanPrefix(ID, "/hostedzone/")
|
|
|
|
}
|
|
|
|
|
|
|
|
// cleanPrefix removes a string prefix from an ID
|
|
|
|
func cleanPrefix(ID, prefix string) string {
|
|
|
|
if strings.HasPrefix(ID, prefix) {
|
|
|
|
ID = strings.TrimPrefix(ID, prefix)
|
|
|
|
}
|
|
|
|
return ID
|
2014-07-22 23:55:19 +02:00
|
|
|
}
|
2015-05-11 17:20:34 +02:00
|
|
|
|
|
|
|
func getNameServers(zoneId string, zoneName string, r53 *route53.Route53) ([]string, error) {
|
|
|
|
resp, err := r53.ListResourceRecordSets(&route53.ListResourceRecordSetsInput{
|
2015-08-17 20:27:16 +02:00
|
|
|
HostedZoneId: aws.String(zoneId),
|
2015-05-11 17:20:34 +02:00
|
|
|
StartRecordName: aws.String(zoneName),
|
|
|
|
StartRecordType: aws.String("NS"),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(resp.ResourceRecordSets) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
ns := make([]string, len(resp.ResourceRecordSets[0].ResourceRecords))
|
|
|
|
for i := range resp.ResourceRecordSets[0].ResourceRecords {
|
|
|
|
ns[i] = *resp.ResourceRecordSets[0].ResourceRecords[i].Value
|
|
|
|
}
|
|
|
|
sort.Strings(ns)
|
|
|
|
return ns, nil
|
|
|
|
}
|