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 }