github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/aws/resource_aws_vpc_dhcp_options.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "time" 8 9 "github.com/aws/aws-sdk-go/aws" 10 "github.com/aws/aws-sdk-go/aws/awserr" 11 "github.com/aws/aws-sdk-go/service/ec2" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/helper/schema" 14 ) 15 16 func resourceAwsVpcDhcpOptions() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceAwsVpcDhcpOptionsCreate, 19 Read: resourceAwsVpcDhcpOptionsRead, 20 Update: resourceAwsVpcDhcpOptionsUpdate, 21 Delete: resourceAwsVpcDhcpOptionsDelete, 22 Importer: &schema.ResourceImporter{ 23 State: schema.ImportStatePassthrough, 24 }, 25 26 Schema: map[string]*schema.Schema{ 27 "domain_name": &schema.Schema{ 28 Type: schema.TypeString, 29 Optional: true, 30 ForceNew: true, 31 }, 32 33 "domain_name_servers": &schema.Schema{ 34 Type: schema.TypeList, 35 Optional: true, 36 ForceNew: true, 37 Elem: &schema.Schema{Type: schema.TypeString}, 38 }, 39 40 "ntp_servers": &schema.Schema{ 41 Type: schema.TypeList, 42 Optional: true, 43 ForceNew: true, 44 Elem: &schema.Schema{Type: schema.TypeString}, 45 }, 46 47 "netbios_node_type": &schema.Schema{ 48 Type: schema.TypeString, 49 Optional: true, 50 ForceNew: true, 51 }, 52 53 "netbios_name_servers": &schema.Schema{ 54 Type: schema.TypeList, 55 Optional: true, 56 ForceNew: true, 57 Elem: &schema.Schema{Type: schema.TypeString}, 58 }, 59 60 "tags": &schema.Schema{ 61 Type: schema.TypeMap, 62 Optional: true, 63 }, 64 }, 65 } 66 } 67 68 func resourceAwsVpcDhcpOptionsCreate(d *schema.ResourceData, meta interface{}) error { 69 conn := meta.(*AWSClient).ec2conn 70 71 setDHCPOption := func(key string) *ec2.NewDhcpConfiguration { 72 log.Printf("[DEBUG] Setting DHCP option %s...", key) 73 tfKey := strings.Replace(key, "-", "_", -1) 74 75 value, ok := d.GetOk(tfKey) 76 if !ok { 77 return nil 78 } 79 80 if v, ok := value.(string); ok { 81 return &ec2.NewDhcpConfiguration{ 82 Key: aws.String(key), 83 Values: []*string{ 84 aws.String(v), 85 }, 86 } 87 } 88 89 if v, ok := value.([]interface{}); ok { 90 var s []*string 91 for _, attr := range v { 92 s = append(s, aws.String(attr.(string))) 93 } 94 95 return &ec2.NewDhcpConfiguration{ 96 Key: aws.String(key), 97 Values: s, 98 } 99 } 100 101 return nil 102 } 103 104 createOpts := &ec2.CreateDhcpOptionsInput{ 105 DhcpConfigurations: []*ec2.NewDhcpConfiguration{ 106 setDHCPOption("domain-name"), 107 setDHCPOption("domain-name-servers"), 108 setDHCPOption("ntp-servers"), 109 setDHCPOption("netbios-node-type"), 110 setDHCPOption("netbios-name-servers"), 111 }, 112 } 113 114 resp, err := conn.CreateDhcpOptions(createOpts) 115 if err != nil { 116 return fmt.Errorf("Error creating DHCP Options Set: %s", err) 117 } 118 119 dos := resp.DhcpOptions 120 d.SetId(*dos.DhcpOptionsId) 121 log.Printf("[INFO] DHCP Options Set ID: %s", d.Id()) 122 123 // Wait for the DHCP Options to become available 124 log.Printf("[DEBUG] Waiting for DHCP Options (%s) to become available", d.Id()) 125 stateConf := &resource.StateChangeConf{ 126 Pending: []string{"pending"}, 127 Target: []string{"created"}, 128 Refresh: resourceDHCPOptionsStateRefreshFunc(conn, d.Id()), 129 Timeout: 1 * time.Minute, 130 } 131 if _, err := stateConf.WaitForState(); err != nil { 132 return fmt.Errorf( 133 "Error waiting for DHCP Options (%s) to become available: %s", 134 d.Id(), err) 135 } 136 137 return resourceAwsVpcDhcpOptionsUpdate(d, meta) 138 } 139 140 func resourceAwsVpcDhcpOptionsRead(d *schema.ResourceData, meta interface{}) error { 141 conn := meta.(*AWSClient).ec2conn 142 req := &ec2.DescribeDhcpOptionsInput{ 143 DhcpOptionsIds: []*string{ 144 aws.String(d.Id()), 145 }, 146 } 147 148 resp, err := conn.DescribeDhcpOptions(req) 149 if err != nil { 150 ec2err, ok := err.(awserr.Error) 151 if !ok { 152 return fmt.Errorf("Error retrieving DHCP Options: %s", err.Error()) 153 } 154 155 if ec2err.Code() == "InvalidDhcpOptionID.NotFound" { 156 log.Printf("[WARN] DHCP Options (%s) not found, removing from state", d.Id()) 157 d.SetId("") 158 return nil 159 } 160 161 return fmt.Errorf("Error retrieving DHCP Options: %s", err.Error()) 162 } 163 164 if len(resp.DhcpOptions) == 0 { 165 return nil 166 } 167 168 opts := resp.DhcpOptions[0] 169 d.Set("tags", tagsToMap(opts.Tags)) 170 171 for _, cfg := range opts.DhcpConfigurations { 172 tfKey := strings.Replace(*cfg.Key, "-", "_", -1) 173 174 if _, ok := d.Get(tfKey).(string); ok { 175 d.Set(tfKey, cfg.Values[0].Value) 176 } else { 177 values := make([]string, 0, len(cfg.Values)) 178 for _, v := range cfg.Values { 179 values = append(values, *v.Value) 180 } 181 182 d.Set(tfKey, values) 183 } 184 } 185 186 return nil 187 } 188 189 func resourceAwsVpcDhcpOptionsUpdate(d *schema.ResourceData, meta interface{}) error { 190 conn := meta.(*AWSClient).ec2conn 191 return setTags(conn, d) 192 } 193 194 func resourceAwsVpcDhcpOptionsDelete(d *schema.ResourceData, meta interface{}) error { 195 conn := meta.(*AWSClient).ec2conn 196 197 return resource.Retry(3*time.Minute, func() *resource.RetryError { 198 log.Printf("[INFO] Deleting DHCP Options ID %s...", d.Id()) 199 _, err := conn.DeleteDhcpOptions(&ec2.DeleteDhcpOptionsInput{ 200 DhcpOptionsId: aws.String(d.Id()), 201 }) 202 203 if err == nil { 204 return nil 205 } 206 207 log.Printf("[WARN] %s", err) 208 209 ec2err, ok := err.(awserr.Error) 210 if !ok { 211 return resource.RetryableError(err) 212 } 213 214 switch ec2err.Code() { 215 case "InvalidDhcpOptionsID.NotFound": 216 return nil 217 case "DependencyViolation": 218 // If it is a dependency violation, we want to disassociate 219 // all VPCs using the given DHCP Options ID, and retry deleting. 220 vpcs, err2 := findVPCsByDHCPOptionsID(conn, d.Id()) 221 if err2 != nil { 222 log.Printf("[ERROR] %s", err2) 223 return resource.RetryableError(err2) 224 } 225 226 for _, vpc := range vpcs { 227 log.Printf("[INFO] Disassociating DHCP Options Set %s from VPC %s...", d.Id(), *vpc.VpcId) 228 if _, err := conn.AssociateDhcpOptions(&ec2.AssociateDhcpOptionsInput{ 229 DhcpOptionsId: aws.String("default"), 230 VpcId: vpc.VpcId, 231 }); err != nil { 232 return resource.RetryableError(err) 233 } 234 } 235 return resource.RetryableError(err) 236 default: 237 return resource.NonRetryableError(err) 238 } 239 }) 240 } 241 242 func findVPCsByDHCPOptionsID(conn *ec2.EC2, id string) ([]*ec2.Vpc, error) { 243 req := &ec2.DescribeVpcsInput{ 244 Filters: []*ec2.Filter{ 245 &ec2.Filter{ 246 Name: aws.String("dhcp-options-id"), 247 Values: []*string{ 248 aws.String(id), 249 }, 250 }, 251 }, 252 } 253 254 resp, err := conn.DescribeVpcs(req) 255 if err != nil { 256 if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpcID.NotFound" { 257 return nil, nil 258 } 259 return nil, err 260 } 261 262 return resp.Vpcs, nil 263 } 264 265 func resourceDHCPOptionsStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { 266 return func() (interface{}, string, error) { 267 DescribeDhcpOpts := &ec2.DescribeDhcpOptionsInput{ 268 DhcpOptionsIds: []*string{ 269 aws.String(id), 270 }, 271 } 272 273 resp, err := conn.DescribeDhcpOptions(DescribeDhcpOpts) 274 if err != nil { 275 if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidDhcpOptionsID.NotFound" { 276 resp = nil 277 } else { 278 log.Printf("Error on DHCPOptionsStateRefresh: %s", err) 279 return nil, "", err 280 } 281 } 282 283 if resp == nil { 284 // Sometimes AWS just has consistency issues and doesn't see 285 // our instance yet. Return an empty state. 286 return nil, "", nil 287 } 288 289 dos := resp.DhcpOptions[0] 290 return dos, "created", nil 291 } 292 }