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