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 }