github.com/nicgrayson/terraform@v0.4.3-0.20150415203910-c4de50829380/builtin/providers/aws/resource_aws_vpc.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/awslabs/aws-sdk-go/aws" 9 "github.com/awslabs/aws-sdk-go/service/ec2" 10 "github.com/hashicorp/terraform/helper/resource" 11 "github.com/hashicorp/terraform/helper/schema" 12 ) 13 14 func resourceAwsVpc() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceAwsVpcCreate, 17 Read: resourceAwsVpcRead, 18 Update: resourceAwsVpcUpdate, 19 Delete: resourceAwsVpcDelete, 20 21 Schema: map[string]*schema.Schema{ 22 "cidr_block": &schema.Schema{ 23 Type: schema.TypeString, 24 Required: true, 25 ForceNew: true, 26 }, 27 28 "instance_tenancy": &schema.Schema{ 29 Type: schema.TypeString, 30 Optional: true, 31 ForceNew: true, 32 }, 33 34 "enable_dns_hostnames": &schema.Schema{ 35 Type: schema.TypeBool, 36 Optional: true, 37 Computed: true, 38 }, 39 40 "enable_dns_support": &schema.Schema{ 41 Type: schema.TypeBool, 42 Optional: true, 43 Computed: true, 44 }, 45 46 "main_route_table_id": &schema.Schema{ 47 Type: schema.TypeString, 48 Computed: true, 49 }, 50 51 "default_network_acl_id": &schema.Schema{ 52 Type: schema.TypeString, 53 Computed: true, 54 }, 55 56 "default_security_group_id": &schema.Schema{ 57 Type: schema.TypeString, 58 Computed: true, 59 }, 60 61 "tags": tagsSchema(), 62 }, 63 } 64 } 65 66 func resourceAwsVpcCreate(d *schema.ResourceData, meta interface{}) error { 67 conn := meta.(*AWSClient).ec2SDKconn 68 instance_tenancy := "default" 69 if v, ok := d.GetOk("instance_tenancy"); ok { 70 instance_tenancy = v.(string) 71 } 72 // Create the VPC 73 createOpts := &ec2.CreateVPCInput{ 74 CIDRBlock: aws.String(d.Get("cidr_block").(string)), 75 InstanceTenancy: aws.String(instance_tenancy), 76 } 77 log.Printf("[DEBUG] VPC create config: %#v", *createOpts) 78 vpcResp, err := conn.CreateVPC(createOpts) 79 if err != nil { 80 return fmt.Errorf("Error creating VPC: %s", err) 81 } 82 83 // Get the ID and store it 84 vpc := vpcResp.VPC 85 d.SetId(*vpc.VPCID) 86 log.Printf("[INFO] VPC ID: %s", d.Id()) 87 88 // Set partial mode and say that we setup the cidr block 89 d.Partial(true) 90 d.SetPartial("cidr_block") 91 92 // Wait for the VPC to become available 93 log.Printf( 94 "[DEBUG] Waiting for VPC (%s) to become available", 95 d.Id()) 96 stateConf := &resource.StateChangeConf{ 97 Pending: []string{"pending"}, 98 Target: "available", 99 Refresh: VPCStateRefreshFunc(conn, d.Id()), 100 Timeout: 10 * time.Minute, 101 } 102 if _, err := stateConf.WaitForState(); err != nil { 103 return fmt.Errorf( 104 "Error waiting for VPC (%s) to become available: %s", 105 d.Id(), err) 106 } 107 108 // Update our attributes and return 109 return resourceAwsVpcUpdate(d, meta) 110 } 111 112 func resourceAwsVpcRead(d *schema.ResourceData, meta interface{}) error { 113 conn := meta.(*AWSClient).ec2SDKconn 114 115 // Refresh the VPC state 116 vpcRaw, _, err := VPCStateRefreshFunc(conn, d.Id())() 117 if err != nil { 118 return err 119 } 120 if vpcRaw == nil { 121 d.SetId("") 122 return nil 123 } 124 125 // VPC stuff 126 vpc := vpcRaw.(*ec2.VPC) 127 vpcid := d.Id() 128 d.Set("cidr_block", vpc.CIDRBlock) 129 130 // Tags 131 d.Set("tags", tagsToMapSDK(vpc.Tags)) 132 133 // Attributes 134 attribute := "enableDnsSupport" 135 DescribeAttrOpts := &ec2.DescribeVPCAttributeInput{ 136 Attribute: aws.String(attribute), 137 VPCID: aws.String(vpcid), 138 } 139 resp, err := conn.DescribeVPCAttribute(DescribeAttrOpts) 140 if err != nil { 141 return err 142 } 143 d.Set("enable_dns_support", *resp.EnableDNSSupport) 144 attribute = "enableDnsHostnames" 145 DescribeAttrOpts = &ec2.DescribeVPCAttributeInput{ 146 Attribute: &attribute, 147 VPCID: &vpcid, 148 } 149 resp, err = conn.DescribeVPCAttribute(DescribeAttrOpts) 150 if err != nil { 151 return err 152 } 153 d.Set("enable_dns_hostnames", *resp.EnableDNSHostnames) 154 155 // Get the main routing table for this VPC 156 // Really Ugly need to make this better - rmenn 157 filter1 := &ec2.Filter{ 158 Name: aws.String("association.main"), 159 Values: []*string{aws.String("true")}, 160 } 161 filter2 := &ec2.Filter{ 162 Name: aws.String("vpc-id"), 163 Values: []*string{aws.String(d.Id())}, 164 } 165 DescribeRouteOpts := &ec2.DescribeRouteTablesInput{ 166 Filters: []*ec2.Filter{filter1, filter2}, 167 } 168 routeResp, err := conn.DescribeRouteTables(DescribeRouteOpts) 169 if err != nil { 170 return err 171 } 172 if v := routeResp.RouteTables; len(v) > 0 { 173 d.Set("main_route_table_id", *v[0].RouteTableID) 174 } 175 176 resourceAwsVpcSetDefaultNetworkAcl(conn, d) 177 resourceAwsVpcSetDefaultSecurityGroup(conn, d) 178 179 return nil 180 } 181 182 func resourceAwsVpcUpdate(d *schema.ResourceData, meta interface{}) error { 183 conn := meta.(*AWSClient).ec2SDKconn 184 185 // Turn on partial mode 186 d.Partial(true) 187 vpcid := d.Id() 188 if d.HasChange("enable_dns_hostnames") { 189 val := d.Get("enable_dns_hostnames").(bool) 190 modifyOpts := &ec2.ModifyVPCAttributeInput{ 191 VPCID: &vpcid, 192 EnableDNSHostnames: &ec2.AttributeBooleanValue{ 193 Value: &val, 194 }, 195 } 196 197 log.Printf( 198 "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", 199 d.Id(), modifyOpts) 200 if _, err := conn.ModifyVPCAttribute(modifyOpts); err != nil { 201 return err 202 } 203 204 d.SetPartial("enable_dns_support") 205 } 206 207 if d.HasChange("enable_dns_support") { 208 val := d.Get("enable_dns_support").(bool) 209 modifyOpts := &ec2.ModifyVPCAttributeInput{ 210 VPCID: &vpcid, 211 EnableDNSSupport: &ec2.AttributeBooleanValue{ 212 Value: &val, 213 }, 214 } 215 216 log.Printf( 217 "[INFO] Modifying enable_dns_support vpc attribute for %s: %#v", 218 d.Id(), modifyOpts) 219 if _, err := conn.ModifyVPCAttribute(modifyOpts); err != nil { 220 return err 221 } 222 223 d.SetPartial("enable_dns_support") 224 } 225 226 if err := setTagsSDK(conn, d); err != nil { 227 return err 228 } else { 229 d.SetPartial("tags") 230 } 231 232 d.Partial(false) 233 return resourceAwsVpcRead(d, meta) 234 } 235 236 func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error { 237 conn := meta.(*AWSClient).ec2SDKconn 238 vpcID := d.Id() 239 DeleteVpcOpts := &ec2.DeleteVPCInput{ 240 VPCID: &vpcID, 241 } 242 log.Printf("[INFO] Deleting VPC: %s", d.Id()) 243 if _, err := conn.DeleteVPC(DeleteVpcOpts); err != nil { 244 ec2err, ok := err.(aws.APIError) 245 if ok && ec2err.Code == "InvalidVpcID.NotFound" { 246 return nil 247 } 248 249 return fmt.Errorf("Error deleting VPC: %s", err) 250 } 251 252 return nil 253 } 254 255 // VPCStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 256 // a VPC. 257 func VPCStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { 258 return func() (interface{}, string, error) { 259 DescribeVpcOpts := &ec2.DescribeVPCsInput{ 260 VPCIDs: []*string{aws.String(id)}, 261 } 262 resp, err := conn.DescribeVPCs(DescribeVpcOpts) 263 if err != nil { 264 if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidVpcID.NotFound" { 265 resp = nil 266 } else { 267 log.Printf("Error on VPCStateRefresh: %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 vpc := resp.VPCs[0] 279 return vpc, *vpc.State, nil 280 } 281 } 282 283 func resourceAwsVpcSetDefaultNetworkAcl(conn *ec2.EC2, d *schema.ResourceData) error { 284 filter1 := &ec2.Filter{ 285 Name: aws.String("default"), 286 Values: []*string{aws.String("true")}, 287 } 288 filter2 := &ec2.Filter{ 289 Name: aws.String("vpc-id"), 290 Values: []*string{aws.String(d.Id())}, 291 } 292 DescribeNetworkACLOpts := &ec2.DescribeNetworkACLsInput{ 293 Filters: []*ec2.Filter{filter1, filter2}, 294 } 295 networkAclResp, err := conn.DescribeNetworkACLs(DescribeNetworkACLOpts) 296 297 if err != nil { 298 return err 299 } 300 if v := networkAclResp.NetworkACLs; len(v) > 0 { 301 d.Set("default_network_acl_id", v[0].NetworkACLID) 302 } 303 304 return nil 305 } 306 307 func resourceAwsVpcSetDefaultSecurityGroup(conn *ec2.EC2, d *schema.ResourceData) error { 308 filter1 := &ec2.Filter{ 309 Name: aws.String("group-name"), 310 Values: []*string{aws.String("default")}, 311 } 312 filter2 := &ec2.Filter{ 313 Name: aws.String("vpc-id"), 314 Values: []*string{aws.String(d.Id())}, 315 } 316 DescribeSgOpts := &ec2.DescribeSecurityGroupsInput{ 317 Filters: []*ec2.Filter{filter1, filter2}, 318 } 319 securityGroupResp, err := conn.DescribeSecurityGroups(DescribeSgOpts) 320 321 if err != nil { 322 return err 323 } 324 if v := securityGroupResp.SecurityGroups; len(v) > 0 { 325 d.Set("default_security_group_id", v[0].GroupID) 326 } 327 328 return nil 329 }