github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/builtin/providers/aws/resource_aws_route53_zone.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "sort" 7 "strings" 8 "time" 9 10 "github.com/hashicorp/terraform/helper/resource" 11 "github.com/hashicorp/terraform/helper/schema" 12 13 "github.com/aws/aws-sdk-go/aws" 14 "github.com/aws/aws-sdk-go/aws/awserr" 15 "github.com/aws/aws-sdk-go/service/route53" 16 ) 17 18 func resourceAwsRoute53Zone() *schema.Resource { 19 return &schema.Resource{ 20 Create: resourceAwsRoute53ZoneCreate, 21 Read: resourceAwsRoute53ZoneRead, 22 Update: resourceAwsRoute53ZoneUpdate, 23 Delete: resourceAwsRoute53ZoneDelete, 24 25 Schema: map[string]*schema.Schema{ 26 "name": &schema.Schema{ 27 Type: schema.TypeString, 28 Required: true, 29 ForceNew: true, 30 }, 31 32 "comment": &schema.Schema{ 33 Type: schema.TypeString, 34 Optional: true, 35 Default: "Managed by Terraform", 36 }, 37 38 "vpc_id": &schema.Schema{ 39 Type: schema.TypeString, 40 Optional: true, 41 ForceNew: true, 42 }, 43 44 "vpc_region": &schema.Schema{ 45 Type: schema.TypeString, 46 Optional: true, 47 ForceNew: true, 48 Computed: true, 49 }, 50 51 "zone_id": &schema.Schema{ 52 Type: schema.TypeString, 53 Computed: true, 54 }, 55 56 "delegation_set_id": &schema.Schema{ 57 Type: schema.TypeString, 58 Optional: true, 59 ForceNew: true, 60 }, 61 62 "name_servers": &schema.Schema{ 63 Type: schema.TypeList, 64 Elem: &schema.Schema{Type: schema.TypeString}, 65 Computed: true, 66 }, 67 68 "tags": tagsSchema(), 69 }, 70 } 71 } 72 73 func resourceAwsRoute53ZoneCreate(d *schema.ResourceData, meta interface{}) error { 74 r53 := meta.(*AWSClient).r53conn 75 76 req := &route53.CreateHostedZoneInput{ 77 Name: aws.String(d.Get("name").(string)), 78 HostedZoneConfig: &route53.HostedZoneConfig{Comment: aws.String(d.Get("comment").(string))}, 79 CallerReference: aws.String(time.Now().Format(time.RFC3339Nano)), 80 } 81 if v := d.Get("vpc_id"); v != "" { 82 req.VPC = &route53.VPC{ 83 VPCId: aws.String(v.(string)), 84 VPCRegion: aws.String(meta.(*AWSClient).region), 85 } 86 if w := d.Get("vpc_region"); w != "" { 87 req.VPC.VPCRegion = aws.String(w.(string)) 88 } 89 d.Set("vpc_region", req.VPC.VPCRegion) 90 } 91 92 if v, ok := d.GetOk("delegation_set_id"); ok { 93 req.DelegationSetId = aws.String(v.(string)) 94 } 95 96 log.Printf("[DEBUG] Creating Route53 hosted zone: %s", *req.Name) 97 var err error 98 resp, err := r53.CreateHostedZone(req) 99 if err != nil { 100 return err 101 } 102 103 // Store the zone_id 104 zone := cleanZoneID(*resp.HostedZone.Id) 105 d.Set("zone_id", zone) 106 d.SetId(zone) 107 108 // Wait until we are done initializing 109 wait := resource.StateChangeConf{ 110 Delay: 30 * time.Second, 111 Pending: []string{"PENDING"}, 112 Target: "INSYNC", 113 Timeout: 10 * time.Minute, 114 MinTimeout: 2 * time.Second, 115 Refresh: func() (result interface{}, state string, err error) { 116 changeRequest := &route53.GetChangeInput{ 117 Id: aws.String(cleanChangeID(*resp.ChangeInfo.Id)), 118 } 119 return resourceAwsGoRoute53Wait(r53, changeRequest) 120 }, 121 } 122 _, err = wait.WaitForState() 123 if err != nil { 124 return err 125 } 126 return resourceAwsRoute53ZoneUpdate(d, meta) 127 } 128 129 func resourceAwsRoute53ZoneRead(d *schema.ResourceData, meta interface{}) error { 130 r53 := meta.(*AWSClient).r53conn 131 zone, err := r53.GetHostedZone(&route53.GetHostedZoneInput{Id: aws.String(d.Id())}) 132 if err != nil { 133 // Handle a deleted zone 134 if r53err, ok := err.(awserr.Error); ok && r53err.Code() == "NoSuchHostedZone" { 135 d.SetId("") 136 return nil 137 } 138 return err 139 } 140 141 if !*zone.HostedZone.Config.PrivateZone { 142 ns := make([]string, len(zone.DelegationSet.NameServers)) 143 for i := range zone.DelegationSet.NameServers { 144 ns[i] = *zone.DelegationSet.NameServers[i] 145 } 146 sort.Strings(ns) 147 if err := d.Set("name_servers", ns); err != nil { 148 return fmt.Errorf("[DEBUG] Error setting name servers for: %s, error: %#v", d.Id(), err) 149 } 150 } else { 151 ns, err := getNameServers(d.Id(), d.Get("name").(string), r53) 152 if err != nil { 153 return err 154 } 155 if err := d.Set("name_servers", ns); err != nil { 156 return fmt.Errorf("[DEBUG] Error setting name servers for: %s, error: %#v", d.Id(), err) 157 } 158 159 var associatedVPC *route53.VPC 160 for _, vpc := range zone.VPCs { 161 if *vpc.VPCId == d.Get("vpc_id") { 162 associatedVPC = vpc 163 } 164 } 165 if associatedVPC == nil { 166 return fmt.Errorf("[DEBUG] VPC: %v is not associated with Zone: %v", d.Get("vpc_id"), d.Id()) 167 } 168 } 169 170 if zone.DelegationSet != nil && zone.DelegationSet.Id != nil { 171 d.Set("delegation_set_id", cleanDelegationSetId(*zone.DelegationSet.Id)) 172 } 173 174 // get tags 175 req := &route53.ListTagsForResourceInput{ 176 ResourceId: aws.String(d.Id()), 177 ResourceType: aws.String("hostedzone"), 178 } 179 180 resp, err := r53.ListTagsForResource(req) 181 if err != nil { 182 return err 183 } 184 185 var tags []*route53.Tag 186 if resp.ResourceTagSet != nil { 187 tags = resp.ResourceTagSet.Tags 188 } 189 190 if err := d.Set("tags", tagsToMapR53(tags)); err != nil { 191 return err 192 } 193 194 return nil 195 } 196 197 func resourceAwsRoute53ZoneUpdate(d *schema.ResourceData, meta interface{}) error { 198 conn := meta.(*AWSClient).r53conn 199 200 if err := setTagsR53(conn, d, "hostedzone"); err != nil { 201 return err 202 } else { 203 d.SetPartial("tags") 204 } 205 206 return resourceAwsRoute53ZoneRead(d, meta) 207 } 208 209 func resourceAwsRoute53ZoneDelete(d *schema.ResourceData, meta interface{}) error { 210 r53 := meta.(*AWSClient).r53conn 211 212 log.Printf("[DEBUG] Deleting Route53 hosted zone: %s (ID: %s)", 213 d.Get("name").(string), d.Id()) 214 _, err := r53.DeleteHostedZone(&route53.DeleteHostedZoneInput{Id: aws.String(d.Id())}) 215 if err != nil { 216 return err 217 } 218 219 return nil 220 } 221 222 func resourceAwsGoRoute53Wait(r53 *route53.Route53, ref *route53.GetChangeInput) (result interface{}, state string, err error) { 223 224 status, err := r53.GetChange(ref) 225 if err != nil { 226 return nil, "UNKNOWN", err 227 } 228 return true, *status.ChangeInfo.Status, nil 229 } 230 231 // cleanChangeID is used to remove the leading /change/ 232 func cleanChangeID(ID string) string { 233 return cleanPrefix(ID, "/change/") 234 } 235 236 // cleanZoneID is used to remove the leading /hostedzone/ 237 func cleanZoneID(ID string) string { 238 return cleanPrefix(ID, "/hostedzone/") 239 } 240 241 // cleanPrefix removes a string prefix from an ID 242 func cleanPrefix(ID, prefix string) string { 243 if strings.HasPrefix(ID, prefix) { 244 ID = strings.TrimPrefix(ID, prefix) 245 } 246 return ID 247 } 248 249 func getNameServers(zoneId string, zoneName string, r53 *route53.Route53) ([]string, error) { 250 resp, err := r53.ListResourceRecordSets(&route53.ListResourceRecordSetsInput{ 251 HostedZoneId: aws.String(zoneId), 252 StartRecordName: aws.String(zoneName), 253 StartRecordType: aws.String("NS"), 254 }) 255 if err != nil { 256 return nil, err 257 } 258 if len(resp.ResourceRecordSets) == 0 { 259 return nil, nil 260 } 261 ns := make([]string, len(resp.ResourceRecordSets[0].ResourceRecords)) 262 for i := range resp.ResourceRecordSets[0].ResourceRecords { 263 ns[i] = *resp.ResourceRecordSets[0].ResourceRecords[i].Value 264 } 265 sort.Strings(ns) 266 return ns, nil 267 }