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