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