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