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