github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/resource_aws_subnet.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 resourceAwsSubnet() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceAwsSubnetCreate,
    18  		Read:   resourceAwsSubnetRead,
    19  		Update: resourceAwsSubnetUpdate,
    20  		Delete: resourceAwsSubnetDelete,
    21  		Importer: &schema.ResourceImporter{
    22  			State: schema.ImportStatePassthrough,
    23  		},
    24  
    25  		Schema: map[string]*schema.Schema{
    26  			"vpc_id": {
    27  				Type:     schema.TypeString,
    28  				Required: true,
    29  				ForceNew: true,
    30  			},
    31  
    32  			"cidr_block": {
    33  				Type:     schema.TypeString,
    34  				Required: true,
    35  				ForceNew: true,
    36  			},
    37  
    38  			"ipv6_cidr_block": {
    39  				Type:     schema.TypeString,
    40  				Optional: true,
    41  				ForceNew: true,
    42  			},
    43  
    44  			"availability_zone": {
    45  				Type:     schema.TypeString,
    46  				Optional: true,
    47  				Computed: true,
    48  				ForceNew: true,
    49  			},
    50  
    51  			"map_public_ip_on_launch": {
    52  				Type:     schema.TypeBool,
    53  				Optional: true,
    54  				Default:  false,
    55  			},
    56  
    57  			"assign_ipv6_address_on_creation": {
    58  				Type:     schema.TypeBool,
    59  				Optional: true,
    60  				Default:  false,
    61  			},
    62  
    63  			"ipv6_cidr_block_association_id": {
    64  				Type:     schema.TypeString,
    65  				Computed: true,
    66  			},
    67  
    68  			"tags": tagsSchema(),
    69  		},
    70  	}
    71  }
    72  
    73  func resourceAwsSubnetCreate(d *schema.ResourceData, meta interface{}) error {
    74  	conn := meta.(*AWSClient).ec2conn
    75  
    76  	createOpts := &ec2.CreateSubnetInput{
    77  		AvailabilityZone: aws.String(d.Get("availability_zone").(string)),
    78  		CidrBlock:        aws.String(d.Get("cidr_block").(string)),
    79  		VpcId:            aws.String(d.Get("vpc_id").(string)),
    80  	}
    81  
    82  	if v, ok := d.GetOk("ipv6_cidr_block"); ok {
    83  		createOpts.Ipv6CidrBlock = aws.String(v.(string))
    84  	}
    85  
    86  	var err error
    87  	resp, err := conn.CreateSubnet(createOpts)
    88  
    89  	if err != nil {
    90  		return fmt.Errorf("Error creating subnet: %s", err)
    91  	}
    92  
    93  	// Get the ID and store it
    94  	subnet := resp.Subnet
    95  	d.SetId(*subnet.SubnetId)
    96  	log.Printf("[INFO] Subnet ID: %s", *subnet.SubnetId)
    97  
    98  	// Wait for the Subnet to become available
    99  	log.Printf("[DEBUG] Waiting for subnet (%s) to become available", *subnet.SubnetId)
   100  	stateConf := &resource.StateChangeConf{
   101  		Pending: []string{"pending"},
   102  		Target:  []string{"available"},
   103  		Refresh: SubnetStateRefreshFunc(conn, *subnet.SubnetId),
   104  		Timeout: 10 * time.Minute,
   105  	}
   106  
   107  	_, err = stateConf.WaitForState()
   108  
   109  	if err != nil {
   110  		return fmt.Errorf(
   111  			"Error waiting for subnet (%s) to become ready: %s",
   112  			d.Id(), err)
   113  	}
   114  
   115  	return resourceAwsSubnetUpdate(d, meta)
   116  }
   117  
   118  func resourceAwsSubnetRead(d *schema.ResourceData, meta interface{}) error {
   119  	conn := meta.(*AWSClient).ec2conn
   120  
   121  	resp, err := conn.DescribeSubnets(&ec2.DescribeSubnetsInput{
   122  		SubnetIds: []*string{aws.String(d.Id())},
   123  	})
   124  
   125  	if err != nil {
   126  		if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidSubnetID.NotFound" {
   127  			// Update state to indicate the subnet no longer exists.
   128  			d.SetId("")
   129  			return nil
   130  		}
   131  		return err
   132  	}
   133  	if resp == nil {
   134  		return nil
   135  	}
   136  
   137  	subnet := resp.Subnets[0]
   138  
   139  	d.Set("vpc_id", subnet.VpcId)
   140  	d.Set("availability_zone", subnet.AvailabilityZone)
   141  	d.Set("cidr_block", subnet.CidrBlock)
   142  	d.Set("map_public_ip_on_launch", subnet.MapPublicIpOnLaunch)
   143  	d.Set("assign_ipv6_address_on_creation", subnet.AssignIpv6AddressOnCreation)
   144  	if subnet.Ipv6CidrBlockAssociationSet != nil {
   145  		d.Set("ipv6_cidr_block", subnet.Ipv6CidrBlockAssociationSet[0].Ipv6CidrBlock)
   146  		d.Set("ipv6_cidr_block_association_id", subnet.Ipv6CidrBlockAssociationSet[0].AssociationId)
   147  	}
   148  	d.Set("tags", tagsToMap(subnet.Tags))
   149  
   150  	return nil
   151  }
   152  
   153  func resourceAwsSubnetUpdate(d *schema.ResourceData, meta interface{}) error {
   154  	conn := meta.(*AWSClient).ec2conn
   155  
   156  	d.Partial(true)
   157  
   158  	if err := setTags(conn, d); err != nil {
   159  		return err
   160  	} else {
   161  		d.SetPartial("tags")
   162  	}
   163  
   164  	if d.HasChange("assign_ipv6_address_on_creation") {
   165  		modifyOpts := &ec2.ModifySubnetAttributeInput{
   166  			SubnetId: aws.String(d.Id()),
   167  			AssignIpv6AddressOnCreation: &ec2.AttributeBooleanValue{
   168  				Value: aws.Bool(d.Get("assign_ipv6_address_on_creation").(bool)),
   169  			},
   170  		}
   171  
   172  		log.Printf("[DEBUG] Subnet modify attributes: %#v", modifyOpts)
   173  
   174  		_, err := conn.ModifySubnetAttribute(modifyOpts)
   175  
   176  		if err != nil {
   177  			return err
   178  		} else {
   179  			d.SetPartial("assign_ipv6_address_on_creation")
   180  		}
   181  	}
   182  
   183  	if d.HasChange("map_public_ip_on_launch") {
   184  		modifyOpts := &ec2.ModifySubnetAttributeInput{
   185  			SubnetId: aws.String(d.Id()),
   186  			MapPublicIpOnLaunch: &ec2.AttributeBooleanValue{
   187  				Value: aws.Bool(d.Get("map_public_ip_on_launch").(bool)),
   188  			},
   189  		}
   190  
   191  		log.Printf("[DEBUG] Subnet modify attributes: %#v", modifyOpts)
   192  
   193  		_, err := conn.ModifySubnetAttribute(modifyOpts)
   194  
   195  		if err != nil {
   196  			return err
   197  		} else {
   198  			d.SetPartial("map_public_ip_on_launch")
   199  		}
   200  	}
   201  
   202  	d.Partial(false)
   203  
   204  	return resourceAwsSubnetRead(d, meta)
   205  }
   206  
   207  func resourceAwsSubnetDelete(d *schema.ResourceData, meta interface{}) error {
   208  	conn := meta.(*AWSClient).ec2conn
   209  
   210  	log.Printf("[INFO] Deleting subnet: %s", d.Id())
   211  	req := &ec2.DeleteSubnetInput{
   212  		SubnetId: aws.String(d.Id()),
   213  	}
   214  
   215  	wait := resource.StateChangeConf{
   216  		Pending:    []string{"pending"},
   217  		Target:     []string{"destroyed"},
   218  		Timeout:    5 * time.Minute,
   219  		MinTimeout: 1 * time.Second,
   220  		Refresh: func() (interface{}, string, error) {
   221  			_, err := conn.DeleteSubnet(req)
   222  			if err != nil {
   223  				if apiErr, ok := err.(awserr.Error); ok {
   224  					if apiErr.Code() == "DependencyViolation" {
   225  						// There is some pending operation, so just retry
   226  						// in a bit.
   227  						return 42, "pending", nil
   228  					}
   229  
   230  					if apiErr.Code() == "InvalidSubnetID.NotFound" {
   231  						return 42, "destroyed", nil
   232  					}
   233  				}
   234  
   235  				return 42, "failure", err
   236  			}
   237  
   238  			return 42, "destroyed", nil
   239  		},
   240  	}
   241  
   242  	if _, err := wait.WaitForState(); err != nil {
   243  		return fmt.Errorf("Error deleting subnet: %s", err)
   244  	}
   245  
   246  	return nil
   247  }
   248  
   249  // SubnetStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch a Subnet.
   250  func SubnetStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
   251  	return func() (interface{}, string, error) {
   252  		resp, err := conn.DescribeSubnets(&ec2.DescribeSubnetsInput{
   253  			SubnetIds: []*string{aws.String(id)},
   254  		})
   255  		if err != nil {
   256  			if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidSubnetID.NotFound" {
   257  				resp = nil
   258  			} else {
   259  				log.Printf("Error on SubnetStateRefresh: %s", err)
   260  				return nil, "", err
   261  			}
   262  		}
   263  
   264  		if resp == nil {
   265  			// Sometimes AWS just has consistency issues and doesn't see
   266  			// our instance yet. Return an empty state.
   267  			return nil, "", nil
   268  		}
   269  
   270  		subnet := resp.Subnets[0]
   271  		return subnet, *subnet.State, nil
   272  	}
   273  }