github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/builtin/providers/aws/resource_aws_vpn_gateway.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/hashicorp/aws-sdk-go/aws" 9 "github.com/hashicorp/aws-sdk-go/gen/ec2" 10 "github.com/hashicorp/terraform/helper/resource" 11 "github.com/hashicorp/terraform/helper/schema" 12 ) 13 14 func resourceAwsVpnGateway() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceAwsVpnGatewayCreate, 17 Read: resourceAwsVpnGatewayRead, 18 Update: resourceAwsVpnGatewayUpdate, 19 Delete: resourceAwsVpnGatewayDelete, 20 21 Schema: map[string]*schema.Schema{ 22 "availability_zone": &schema.Schema{ 23 Type: schema.TypeString, 24 Optional: true, 25 ForceNew: true, 26 }, 27 28 "vpc_id": &schema.Schema{ 29 Type: schema.TypeString, 30 Optional: true, 31 }, 32 33 "tags": tagsSchema(), 34 }, 35 } 36 } 37 38 func resourceAwsVpnGatewayCreate(d *schema.ResourceData, meta interface{}) error { 39 ec2conn := meta.(*AWSClient).ec2conn 40 41 createOpts := &ec2.CreateVPNGatewayRequest{ 42 AvailabilityZone: aws.String(d.Get("availability_zone").(string)), 43 Type: aws.String("ipsec.1"), 44 } 45 46 // Create the VPN gateway 47 log.Printf("[DEBUG] Creating VPN gateway") 48 resp, err := ec2conn.CreateVPNGateway(createOpts) 49 if err != nil { 50 return fmt.Errorf("Error creating VPN gateway: %s", err) 51 } 52 53 // Get the ID and store it 54 vpnGateway := resp.VPNGateway 55 d.SetId(*vpnGateway.VPNGatewayID) 56 log.Printf("[INFO] VPN Gateway ID: %s", *vpnGateway.VPNGatewayID) 57 58 // Attach the VPN gateway to the correct VPC 59 return resourceAwsVpnGatewayUpdate(d, meta) 60 } 61 62 func resourceAwsVpnGatewayRead(d *schema.ResourceData, meta interface{}) error { 63 ec2conn := meta.(*AWSClient).ec2conn 64 65 vpnGatewayRaw, _, err := vpnGatewayStateRefreshFunc(ec2conn, d.Id())() 66 if err != nil { 67 return err 68 } 69 if vpnGatewayRaw == nil { 70 // Seems we have lost our VPN gateway 71 d.SetId("") 72 return nil 73 } 74 75 vpnGateway := vpnGatewayRaw.(*ec2.VPNGateway) 76 if len(vpnGateway.VPCAttachments) == 0 { 77 // Gateway exists but not attached to the VPC 78 d.Set("vpc_id", "") 79 } else { 80 d.Set("vpc_id", vpnGateway.VPCAttachments[0].VPCID) 81 } 82 d.Set("availability_zone", vpnGateway.AvailabilityZone) 83 d.Set("tags", tagsToMap(vpnGateway.Tags)) 84 85 return nil 86 } 87 88 func resourceAwsVpnGatewayUpdate(d *schema.ResourceData, meta interface{}) error { 89 if d.HasChange("vpc_id") { 90 // If we're already attached, detach it first 91 if err := resourceAwsVpnGatewayDetach(d, meta); err != nil { 92 return err 93 } 94 95 // Attach the VPN gateway to the new vpc 96 if err := resourceAwsVpnGatewayAttach(d, meta); err != nil { 97 return err 98 } 99 } 100 101 ec2conn := meta.(*AWSClient).ec2conn 102 103 if err := setTags(ec2conn, d); err != nil { 104 return err 105 } 106 107 d.SetPartial("tags") 108 109 return resourceAwsVpnGatewayRead(d, meta) 110 } 111 112 func resourceAwsVpnGatewayDelete(d *schema.ResourceData, meta interface{}) error { 113 ec2conn := meta.(*AWSClient).ec2conn 114 115 // Detach if it is attached 116 if err := resourceAwsVpnGatewayDetach(d, meta); err != nil { 117 return err 118 } 119 120 log.Printf("[INFO] Deleting VPN gateway: %s", d.Id()) 121 122 return resource.Retry(5*time.Minute, func() error { 123 err := ec2conn.DeleteVPNGateway(&ec2.DeleteVPNGatewayRequest{ 124 VPNGatewayID: aws.String(d.Id()), 125 }) 126 if err == nil { 127 return nil 128 } 129 130 ec2err, ok := err.(aws.APIError) 131 if !ok { 132 return err 133 } 134 135 switch ec2err.Code { 136 case "InvalidVpnGatewayID.NotFound": 137 return nil 138 case "IncorrectState": 139 return err // retry 140 } 141 142 return resource.RetryError{Err: err} 143 }) 144 } 145 146 func resourceAwsVpnGatewayAttach(d *schema.ResourceData, meta interface{}) error { 147 ec2conn := meta.(*AWSClient).ec2conn 148 149 if d.Get("vpc_id").(string) == "" { 150 log.Printf( 151 "[DEBUG] Not attaching VPN Gateway '%s' as no VPC ID is set", 152 d.Id()) 153 return nil 154 } 155 156 log.Printf( 157 "[INFO] Attaching VPN Gateway '%s' to VPC '%s'", 158 d.Id(), 159 d.Get("vpc_id").(string)) 160 161 _, err := ec2conn.AttachVPNGateway(&ec2.AttachVPNGatewayRequest{ 162 VPNGatewayID: aws.String(d.Id()), 163 VPCID: aws.String(d.Get("vpc_id").(string)), 164 }) 165 if err != nil { 166 return err 167 } 168 169 // A note on the states below: the AWS docs (as of July, 2014) say 170 // that the states would be: attached, attaching, detached, detaching, 171 // but when running, I noticed that the state is usually "available" when 172 // it is attached. 173 174 // Wait for it to be fully attached before continuing 175 log.Printf("[DEBUG] Waiting for VPN gateway (%s) to attach", d.Id()) 176 stateConf := &resource.StateChangeConf{ 177 Pending: []string{"detached", "attaching"}, 178 Target: "available", 179 Refresh: VpnGatewayAttachStateRefreshFunc(ec2conn, d.Id(), "available"), 180 Timeout: 1 * time.Minute, 181 } 182 if _, err := stateConf.WaitForState(); err != nil { 183 return fmt.Errorf( 184 "Error waiting for VPN gateway (%s) to attach: %s", 185 d.Id(), err) 186 } 187 188 return nil 189 } 190 191 func resourceAwsVpnGatewayDetach(d *schema.ResourceData, meta interface{}) error { 192 ec2conn := meta.(*AWSClient).ec2conn 193 194 // Get the old VPC ID to detach from 195 vpcID, _ := d.GetChange("vpc_id") 196 197 if vpcID.(string) == "" { 198 log.Printf( 199 "[DEBUG] Not detaching VPN Gateway '%s' as no VPC ID is set", 200 d.Id()) 201 return nil 202 } 203 204 log.Printf( 205 "[INFO] Detaching VPN Gateway '%s' from VPC '%s'", 206 d.Id(), 207 vpcID.(string)) 208 209 wait := true 210 err := ec2conn.DetachVPNGateway(&ec2.DetachVPNGatewayRequest{ 211 VPNGatewayID: aws.String(d.Id()), 212 VPCID: aws.String(d.Get("vpc_id").(string)), 213 }) 214 if err != nil { 215 ec2err, ok := err.(aws.APIError) 216 if ok { 217 if ec2err.Code == "InvalidVpnGatewayID.NotFound" { 218 err = nil 219 wait = false 220 } else if ec2err.Code == "InvalidVpnGatewayAttachment.NotFound" { 221 err = nil 222 wait = false 223 } 224 } 225 226 if err != nil { 227 return err 228 } 229 } 230 231 if !wait { 232 return nil 233 } 234 235 // Wait for it to be fully detached before continuing 236 log.Printf("[DEBUG] Waiting for VPN gateway (%s) to detach", d.Id()) 237 stateConf := &resource.StateChangeConf{ 238 Pending: []string{"attached", "detaching", "available"}, 239 Target: "detached", 240 Refresh: VpnGatewayAttachStateRefreshFunc(ec2conn, d.Id(), "detached"), 241 Timeout: 1 * time.Minute, 242 } 243 if _, err := stateConf.WaitForState(); err != nil { 244 return fmt.Errorf( 245 "Error waiting for vpn gateway (%s) to detach: %s", 246 d.Id(), err) 247 } 248 249 return nil 250 } 251 252 // vpnGatewayStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch a VPNGateway. 253 func vpnGatewayStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { 254 return func() (interface{}, string, error) { 255 resp, err := conn.DescribeVPNGateways(&ec2.DescribeVPNGatewaysRequest{ 256 VPNGatewayIDs: []string{id}, 257 }) 258 if err != nil { 259 if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidVpnGatewayID.NotFound" { 260 resp = nil 261 } else { 262 log.Printf("[ERROR] Error on VpnGatewayStateRefresh: %s", err) 263 return nil, "", err 264 } 265 } 266 267 if resp == nil { 268 // Sometimes AWS just has consistency issues and doesn't see 269 // our instance yet. Return an empty state. 270 return nil, "", nil 271 } 272 273 vpnGateway := &resp.VPNGateways[0] 274 return vpnGateway, *vpnGateway.State, nil 275 } 276 } 277 278 // VpnGatewayAttachStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 279 // the state of a VPN gateway's attachment 280 func VpnGatewayAttachStateRefreshFunc(conn *ec2.EC2, id string, expected string) resource.StateRefreshFunc { 281 var start time.Time 282 return func() (interface{}, string, error) { 283 if start.IsZero() { 284 start = time.Now() 285 } 286 287 resp, err := conn.DescribeVPNGateways(&ec2.DescribeVPNGatewaysRequest{ 288 VPNGatewayIDs: []string{id}, 289 }) 290 if err != nil { 291 if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidVpnGatewayID.NotFound" { 292 resp = nil 293 } else { 294 log.Printf("[ERROR] Error on VpnGatewayStateRefresh: %s", err) 295 return nil, "", err 296 } 297 } 298 299 if resp == nil { 300 // Sometimes AWS just has consistency issues and doesn't see 301 // our instance yet. Return an empty state. 302 return nil, "", nil 303 } 304 305 vpnGateway := &resp.VPNGateways[0] 306 307 if time.Now().Sub(start) > 10*time.Second { 308 return vpnGateway, expected, nil 309 } 310 311 if len(vpnGateway.VPCAttachments) == 0 { 312 // No attachments, we're detached 313 return vpnGateway, "detached", nil 314 } 315 316 return vpnGateway, *vpnGateway.VPCAttachments[0].State, nil 317 } 318 }