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