github.com/keshavdv/terraform@v0.7.0-rc2.0.20160711232630-d69256dcb425/builtin/providers/aws/resource_aws_vpc.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/aws/awserr" 10 "github.com/aws/aws-sdk-go/service/ec2" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceAwsVpc() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAwsVpcCreate, 18 Read: resourceAwsVpcRead, 19 Update: resourceAwsVpcUpdate, 20 Delete: resourceAwsVpcDelete, 21 Importer: &schema.ResourceImporter{ 22 State: schema.ImportStatePassthrough, 23 }, 24 25 Schema: map[string]*schema.Schema{ 26 "cidr_block": &schema.Schema{ 27 Type: schema.TypeString, 28 Required: true, 29 ForceNew: true, 30 ValidateFunc: validateCIDRNetworkAddress, 31 }, 32 33 "instance_tenancy": &schema.Schema{ 34 Type: schema.TypeString, 35 Optional: true, 36 ForceNew: true, 37 Computed: true, 38 }, 39 40 "enable_dns_hostnames": &schema.Schema{ 41 Type: schema.TypeBool, 42 Optional: true, 43 Computed: true, 44 }, 45 46 "enable_dns_support": &schema.Schema{ 47 Type: schema.TypeBool, 48 Optional: true, 49 Computed: true, 50 }, 51 52 "enable_classiclink": &schema.Schema{ 53 Type: schema.TypeBool, 54 Optional: true, 55 Computed: true, 56 }, 57 58 "main_route_table_id": &schema.Schema{ 59 Type: schema.TypeString, 60 Computed: true, 61 }, 62 63 "default_network_acl_id": &schema.Schema{ 64 Type: schema.TypeString, 65 Computed: true, 66 }, 67 68 "dhcp_options_id": &schema.Schema{ 69 Type: schema.TypeString, 70 Computed: true, 71 }, 72 73 "default_security_group_id": &schema.Schema{ 74 Type: schema.TypeString, 75 Computed: true, 76 }, 77 78 "tags": tagsSchema(), 79 }, 80 } 81 } 82 83 func resourceAwsVpcCreate(d *schema.ResourceData, meta interface{}) error { 84 conn := meta.(*AWSClient).ec2conn 85 instance_tenancy := "default" 86 if v, ok := d.GetOk("instance_tenancy"); ok { 87 instance_tenancy = v.(string) 88 } 89 // Create the VPC 90 createOpts := &ec2.CreateVpcInput{ 91 CidrBlock: aws.String(d.Get("cidr_block").(string)), 92 InstanceTenancy: aws.String(instance_tenancy), 93 } 94 log.Printf("[DEBUG] VPC create config: %#v", *createOpts) 95 vpcResp, err := conn.CreateVpc(createOpts) 96 if err != nil { 97 return fmt.Errorf("Error creating VPC: %s", err) 98 } 99 100 // Get the ID and store it 101 vpc := vpcResp.Vpc 102 d.SetId(*vpc.VpcId) 103 log.Printf("[INFO] VPC ID: %s", d.Id()) 104 105 // Set partial mode and say that we setup the cidr block 106 d.Partial(true) 107 d.SetPartial("cidr_block") 108 109 // Wait for the VPC to become available 110 log.Printf( 111 "[DEBUG] Waiting for VPC (%s) to become available", 112 d.Id()) 113 stateConf := &resource.StateChangeConf{ 114 Pending: []string{"pending"}, 115 Target: []string{"available"}, 116 Refresh: VPCStateRefreshFunc(conn, d.Id()), 117 Timeout: 10 * time.Minute, 118 } 119 if _, err := stateConf.WaitForState(); err != nil { 120 return fmt.Errorf( 121 "Error waiting for VPC (%s) to become available: %s", 122 d.Id(), err) 123 } 124 125 // Update our attributes and return 126 return resourceAwsVpcUpdate(d, meta) 127 } 128 129 func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error { 130 conn := meta.(*AWSClient).ec2conn 131 132 // Refresh the VPC state 133 vpcRaw, _, err := VPCStateRefreshFunc(conn, d.Id())() 134 if err != nil { 135 return err 136 } 137 if vpcRaw == nil { 138 d.SetId("") 139 return nil 140 } 141 142 // VPC stuff 143 vpc := vpcRaw.(*ec2.Vpc) 144 vpcid := d.Id() 145 d.Set("cidr_block", vpc.CidrBlock) 146 d.Set("dhcp_options_id", vpc.DhcpOptionsId) 147 d.Set("instance_tenancy", vpc.InstanceTenancy) 148 149 // Tags 150 d.Set("tags", tagsToMap(vpc.Tags)) 151 152 // Attributes 153 attribute := "enableDnsSupport" 154 DescribeAttrOpts := &ec2.DescribeVpcAttributeInput{ 155 Attribute: aws.String(attribute), 156 VpcId: aws.String(vpcid), 157 } 158 resp, err := conn.DescribeVpcAttribute(DescribeAttrOpts) 159 if err != nil { 160 return err 161 } 162 d.Set("enable_dns_support", *resp.EnableDnsSupport.Value) 163 attribute = "enableDnsHostnames" 164 DescribeAttrOpts = &ec2.DescribeVpcAttributeInput{ 165 Attribute: &attribute, 166 VpcId: &vpcid, 167 } 168 resp, err = conn.DescribeVpcAttribute(DescribeAttrOpts) 169 if err != nil { 170 return err 171 } 172 d.Set("enable_dns_hostnames", *resp.EnableDnsHostnames.Value) 173 174 DescribeClassiclinkOpts := &ec2.DescribeVpcClassicLinkInput{ 175 VpcIds: []*string{&vpcid}, 176 } 177 178 // Classic Link is only available in regions that support EC2 Classic 179 respClassiclink, err := conn.DescribeVpcClassicLink(DescribeClassiclinkOpts) 180 if err != nil { 181 if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "UnsupportedOperation" { 182 log.Printf("[WARN] VPC Classic Link is not supported in this region") 183 } else { 184 return err 185 } 186 } else { 187 classiclink_enabled := false 188 for _, v := range respClassiclink.Vpcs { 189 if *v.VpcId == vpcid { 190 if v.ClassicLinkEnabled != nil { 191 classiclink_enabled = *v.ClassicLinkEnabled 192 } 193 break 194 } 195 } 196 d.Set("enable_classiclink", classiclink_enabled) 197 } 198 199 // Get the main routing table for this VPC 200 // Really Ugly need to make this better - rmenn 201 filter1 := &ec2.Filter{ 202 Name: aws.String("association.main"), 203 Values: []*string{aws.String("true")}, 204 } 205 filter2 := &ec2.Filter{ 206 Name: aws.String("vpc-id"), 207 Values: []*string{aws.String(d.Id())}, 208 } 209 DescribeRouteOpts := &ec2.DescribeRouteTablesInput{ 210 Filters: []*ec2.Filter{filter1, filter2}, 211 } 212 routeResp, err := conn.DescribeRouteTables(DescribeRouteOpts) 213 if err != nil { 214 return err 215 } 216 if v := routeResp.RouteTables; len(v) > 0 { 217 d.Set("main_route_table_id", *v[0].RouteTableId) 218 } 219 220 resourceAwsVpcSetDefaultNetworkAcl(conn, d) 221 resourceAwsVpcSetDefaultSecurityGroup(conn, d) 222 223 return nil 224 } 225 226 func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { 227 conn := meta.(*AWSClient).ec2conn 228 229 // Turn on partial mode 230 d.Partial(true) 231 vpcid := d.Id() 232 if d.HasChange("enable_dns_hostnames") { 233 val := d.Get("enable_dns_hostnames").(bool) 234 modifyOpts := &ec2.ModifyVpcAttributeInput{ 235 VpcId: &vpcid, 236 EnableDnsHostnames: &ec2.AttributeBooleanValue{ 237 Value: &val, 238 }, 239 } 240 241 log.Printf( 242 "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", 243 d.Id(), modifyOpts) 244 if _, err := conn.ModifyVpcAttribute(modifyOpts); err != nil { 245 return err 246 } 247 248 d.SetPartial("enable_dns_support") 249 } 250 251 if d.HasChange("enable_dns_support") { 252 val := d.Get("enable_dns_support").(bool) 253 modifyOpts := &ec2.ModifyVpcAttributeInput{ 254 VpcId: &vpcid, 255 EnableDnsSupport: &ec2.AttributeBooleanValue{ 256 Value: &val, 257 }, 258 } 259 260 log.Printf( 261 "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", 262 d.Id(), modifyOpts) 263 if _, err := conn.ModifyVpcAttribute(modifyOpts); err != nil { 264 return err 265 } 266 267 d.SetPartial("enable_dns_support") 268 } 269 270 if d.HasChange("enable_classiclink") { 271 val := d.Get("enable_classiclink").(bool) 272 273 if val { 274 modifyOpts := &ec2.EnableVpcClassicLinkInput{ 275 VpcId: &vpcid, 276 } 277 log.Printf( 278 "[INFO] Modifying enable_classiclink vpc attribute for %s: %#v", 279 d.Id(), modifyOpts) 280 if _, err := conn.EnableVpcClassicLink(modifyOpts); err != nil { 281 return err 282 } 283 } else { 284 modifyOpts := &ec2.DisableVpcClassicLinkInput{ 285 VpcId: &vpcid, 286 } 287 log.Printf( 288 "[INFO] Modifying enable_classiclink vpc attribute for %s: %#v", 289 d.Id(), modifyOpts) 290 if _, err := conn.DisableVpcClassicLink(modifyOpts); err != nil { 291 return err 292 } 293 } 294 295 d.SetPartial("enable_classiclink") 296 } 297 298 if err := setTags(conn, d); err != nil { 299 return err 300 } else { 301 d.SetPartial("tags") 302 } 303 304 d.Partial(false) 305 return resourceAwsVpcRead(d, meta) 306 } 307 308 func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error { 309 conn := meta.(*AWSClient).ec2conn 310 vpcID := d.Id() 311 DeleteVpcOpts := &ec2.DeleteVpcInput{ 312 VpcId: &vpcID, 313 } 314 log.Printf("[INFO] Deleting VPC: %s", d.Id()) 315 316 return resource.Retry(5*time.Minute, func() *resource.RetryError { 317 _, err := conn.DeleteVpc(DeleteVpcOpts) 318 if err == nil { 319 return nil 320 } 321 322 ec2err, ok := err.(awserr.Error) 323 if !ok { 324 return resource.NonRetryableError(err) 325 } 326 327 switch ec2err.Code() { 328 case "InvalidVpcID.NotFound": 329 return nil 330 case "DependencyViolation": 331 return resource.RetryableError(err) 332 } 333 334 return resource.NonRetryableError(fmt.Errorf("Error deleting VPC: %s", err)) 335 }) 336 } 337 338 // VPCStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 339 // a VPC. 340 func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { 341 return func() (interface{}, string, error) { 342 DescribeVpcOpts := &ec2.DescribeVpcsInput{ 343 VpcIds: []*string{aws.String(id)}, 344 } 345 resp, err := conn.DescribeVpcs(DescribeVpcOpts) 346 if err != nil { 347 if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpcID.NotFound" { 348 resp = nil 349 } else { 350 log.Printf("Error on VPCStateRefresh: %s", err) 351 return nil, "", err 352 } 353 } 354 355 if resp == nil { 356 // Sometimes AWS just has consistency issues and doesn't see 357 // our instance yet. Return an empty state. 358 return nil, "", nil 359 } 360 361 vpc := resp.Vpcs[0] 362 return vpc, *vpc.State, nil 363 } 364 } 365 366 func resourceAwsVpcSetDefaultNetworkAcl(conn *ec2.EC2, d *schema.ResourceData) error { 367 filter1 := &ec2.Filter{ 368 Name: aws.String("default"), 369 Values: []*string{aws.String("true")}, 370 } 371 filter2 := &ec2.Filter{ 372 Name: aws.String("vpc-id"), 373 Values: []*string{aws.String(d.Id())}, 374 } 375 DescribeNetworkACLOpts := &ec2.DescribeNetworkAclsInput{ 376 Filters: []*ec2.Filter{filter1, filter2}, 377 } 378 networkAclResp, err := conn.DescribeNetworkAcls(DescribeNetworkACLOpts) 379 380 if err != nil { 381 return err 382 } 383 if v := networkAclResp.NetworkAcls; len(v) > 0 { 384 d.Set("default_network_acl_id", v[0].NetworkAclId) 385 } 386 387 return nil 388 } 389 390 func resourceAwsVpcSetDefaultSecurityGroup(conn *ec2.EC2, d *schema.ResourceData) error { 391 filter1 := &ec2.Filter{ 392 Name: aws.String("group-name"), 393 Values: []*string{aws.String("default")}, 394 } 395 filter2 := &ec2.Filter{ 396 Name: aws.String("vpc-id"), 397 Values: []*string{aws.String(d.Id())}, 398 } 399 DescribeSgOpts := &ec2.DescribeSecurityGroupsInput{ 400 Filters: []*ec2.Filter{filter1, filter2}, 401 } 402 securityGroupResp, err := conn.DescribeSecurityGroups(DescribeSgOpts) 403 404 if err != nil { 405 return err 406 } 407 if v := securityGroupResp.SecurityGroups; len(v) > 0 { 408 d.Set("default_security_group_id", v[0].GroupId) 409 } 410 411 return nil 412 }