Add top-level ELB Attachment resource
Add an aws_elb_attachment resource so that the attment of instances to an ELB can be managed separately from an aws_elb and prevent dependency cycles.
This commit is contained in:
parent
f891ab81f4
commit
e4d8c6929f
|
@ -176,6 +176,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
"aws_elastic_beanstalk_environment": resourceAwsElasticBeanstalkEnvironment(),
|
"aws_elastic_beanstalk_environment": resourceAwsElasticBeanstalkEnvironment(),
|
||||||
"aws_elasticsearch_domain": resourceAwsElasticSearchDomain(),
|
"aws_elasticsearch_domain": resourceAwsElasticSearchDomain(),
|
||||||
"aws_elb": resourceAwsElb(),
|
"aws_elb": resourceAwsElb(),
|
||||||
|
"aws_elb_attachment": resourceAwsElbAttachment(),
|
||||||
"aws_flow_log": resourceAwsFlowLog(),
|
"aws_flow_log": resourceAwsFlowLog(),
|
||||||
"aws_glacier_vault": resourceAwsGlacierVault(),
|
"aws_glacier_vault": resourceAwsGlacierVault(),
|
||||||
"aws_iam_access_key": resourceAwsIamAccessKey(),
|
"aws_iam_access_key": resourceAwsIamAccessKey(),
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/elb"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsElbAttachment() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsElbAttachmentCreate,
|
||||||
|
Read: resourceAwsElbAttachmentRead,
|
||||||
|
Delete: resourceAwsElbAttachmentDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"elb": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
ForceNew: true,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"instance": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
ForceNew: true,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElbAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
elbconn := meta.(*AWSClient).elbconn
|
||||||
|
elbName := d.Get("elb").(string)
|
||||||
|
|
||||||
|
instance := d.Get("instance").(string)
|
||||||
|
|
||||||
|
registerInstancesOpts := elb.RegisterInstancesWithLoadBalancerInput{
|
||||||
|
LoadBalancerName: aws.String(elbName),
|
||||||
|
Instances: []*elb.Instance{{InstanceId: aws.String(instance)}},
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[INFO] registering instance %s with ELB %s", instance, elbName)
|
||||||
|
|
||||||
|
_, err := elbconn.RegisterInstancesWithLoadBalancer(®isterInstancesOpts)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failure registering instances with ELB: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(resource.PrefixedUniqueId(fmt.Sprintf("%s-", elbName)))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElbAttachmentRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
elbconn := meta.(*AWSClient).elbconn
|
||||||
|
elbName := d.Get("elb").(string)
|
||||||
|
|
||||||
|
// only add the instance that was previously defined for this resource
|
||||||
|
expected := d.Get("instance").(string)
|
||||||
|
|
||||||
|
// Retrieve the ELB properties to get a list of attachments
|
||||||
|
describeElbOpts := &elb.DescribeLoadBalancersInput{
|
||||||
|
LoadBalancerNames: []*string{aws.String(elbName)},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := elbconn.DescribeLoadBalancers(describeElbOpts)
|
||||||
|
if err != nil {
|
||||||
|
if isLoadBalancerNotFound(err) {
|
||||||
|
log.Printf("[ERROR] ELB %s not found", elbName)
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Error retrieving ELB: %s", err)
|
||||||
|
}
|
||||||
|
if len(resp.LoadBalancerDescriptions) != 1 {
|
||||||
|
log.Printf("[ERROR] Unable to find ELB: %s", resp.LoadBalancerDescriptions)
|
||||||
|
d.SetId("")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// only set the instance Id that this resource manages
|
||||||
|
found := false
|
||||||
|
for _, i := range resp.LoadBalancerDescriptions[0].Instances {
|
||||||
|
if expected == *i.InstanceId {
|
||||||
|
d.Set("instance", expected)
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
log.Printf("[WARN] instance %s not found in elb attachments", expected)
|
||||||
|
d.SetId("")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElbAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
elbconn := meta.(*AWSClient).elbconn
|
||||||
|
elbName := d.Get("elb").(string)
|
||||||
|
|
||||||
|
instance := d.Get("instance").(string)
|
||||||
|
|
||||||
|
log.Printf("[INFO] Deleting Attachment %s from: %s", instance, elbName)
|
||||||
|
|
||||||
|
deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancerInput{
|
||||||
|
LoadBalancerName: aws.String(elbName),
|
||||||
|
Instances: []*elb.Instance{{InstanceId: aws.String(instance)}},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := elbconn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failure deregistering instances from ELB: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,232 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/service/elb"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSELBAttachment_basic(t *testing.T) {
|
||||||
|
var conf elb.LoadBalancerDescription
|
||||||
|
|
||||||
|
testCheckInstanceAttached := func(count int) resource.TestCheckFunc {
|
||||||
|
return func(*terraform.State) error {
|
||||||
|
if len(conf.Instances) != count {
|
||||||
|
return fmt.Errorf("instance count does not match")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
IDRefreshName: "aws_elb.bar",
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSELBDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSELBAttachmentConfig1,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSELBExists("aws_elb.bar", &conf),
|
||||||
|
testCheckInstanceAttached(1),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSELBAttachmentConfig2,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSELBExists("aws_elb.bar", &conf),
|
||||||
|
testCheckInstanceAttached(2),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSELBAttachmentConfig3,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSELBExists("aws_elb.bar", &conf),
|
||||||
|
testCheckInstanceAttached(2),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSELBAttachmentConfig4,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSELBExists("aws_elb.bar", &conf),
|
||||||
|
testCheckInstanceAttached(0),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove and instance and check that it's correctly re-attached.
|
||||||
|
func TestAccAWSELBAttachment_drift(t *testing.T) {
|
||||||
|
var conf elb.LoadBalancerDescription
|
||||||
|
|
||||||
|
deregInstance := func() {
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).elbconn
|
||||||
|
|
||||||
|
deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancerInput{
|
||||||
|
LoadBalancerName: conf.LoadBalancerName,
|
||||||
|
Instances: conf.Instances,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] deregistering instance %s from ELB", conf.Instances[0].InstanceId)
|
||||||
|
|
||||||
|
_, err := conn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failure deregistering instances from ELB: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
testCheckInstanceAttached := func(count int) resource.TestCheckFunc {
|
||||||
|
return func(*terraform.State) error {
|
||||||
|
if len(conf.Instances) != count {
|
||||||
|
return fmt.Errorf("instance count does not match")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
IDRefreshName: "aws_elb.bar",
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSELBDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSELBAttachmentConfig1,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSELBExists("aws_elb.bar", &conf),
|
||||||
|
testCheckInstanceAttached(1),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
// remove an instance from the ELB, and make sure it gets re-added
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSELBAttachmentConfig1,
|
||||||
|
PreConfig: deregInstance,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSELBExists("aws_elb.bar", &conf),
|
||||||
|
testCheckInstanceAttached(1),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// add one attachment
|
||||||
|
const testAccAWSELBAttachmentConfig1 = `
|
||||||
|
resource "aws_elb" "bar" {
|
||||||
|
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
|
||||||
|
|
||||||
|
listener {
|
||||||
|
instance_port = 8000
|
||||||
|
instance_protocol = "http"
|
||||||
|
lb_port = 80
|
||||||
|
lb_protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo1" {
|
||||||
|
# us-west-2
|
||||||
|
ami = "ami-043a5034"
|
||||||
|
instance_type = "t1.micro"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_elb_attachment" "foo1" {
|
||||||
|
elb = "${aws_elb.bar.id}"
|
||||||
|
instance = "${aws_instance.foo1.id}"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// add a second attachment
|
||||||
|
const testAccAWSELBAttachmentConfig2 = `
|
||||||
|
resource "aws_elb" "bar" {
|
||||||
|
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
|
||||||
|
|
||||||
|
listener {
|
||||||
|
instance_port = 8000
|
||||||
|
instance_protocol = "http"
|
||||||
|
lb_port = 80
|
||||||
|
lb_protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo1" {
|
||||||
|
# us-west-2
|
||||||
|
ami = "ami-043a5034"
|
||||||
|
instance_type = "t1.micro"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo2" {
|
||||||
|
# us-west-2
|
||||||
|
ami = "ami-043a5034"
|
||||||
|
instance_type = "t1.micro"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_elb_attachment" "foo1" {
|
||||||
|
elb = "${aws_elb.bar.id}"
|
||||||
|
instance = "${aws_instance.foo1.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_elb_attachment" "foo2" {
|
||||||
|
elb = "${aws_elb.bar.id}"
|
||||||
|
instance = "${aws_instance.foo2.id}"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// swap attachments between resources
|
||||||
|
const testAccAWSELBAttachmentConfig3 = `
|
||||||
|
resource "aws_elb" "bar" {
|
||||||
|
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
|
||||||
|
|
||||||
|
listener {
|
||||||
|
instance_port = 8000
|
||||||
|
instance_protocol = "http"
|
||||||
|
lb_port = 80
|
||||||
|
lb_protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo1" {
|
||||||
|
# us-west-2
|
||||||
|
ami = "ami-043a5034"
|
||||||
|
instance_type = "t1.micro"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo2" {
|
||||||
|
# us-west-2
|
||||||
|
ami = "ami-043a5034"
|
||||||
|
instance_type = "t1.micro"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_elb_attachment" "foo1" {
|
||||||
|
elb = "${aws_elb.bar.id}"
|
||||||
|
instance = "${aws_instance.foo2.id}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_elb_attachment" "foo2" {
|
||||||
|
elb = "${aws_elb.bar.id}"
|
||||||
|
instance = "${aws_instance.foo1.id}"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// destroy attachments
|
||||||
|
const testAccAWSELBAttachmentConfig4 = `
|
||||||
|
resource "aws_elb" "bar" {
|
||||||
|
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
|
||||||
|
|
||||||
|
listener {
|
||||||
|
instance_port = 8000
|
||||||
|
instance_protocol = "http"
|
||||||
|
lb_port = 80
|
||||||
|
lb_protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -10,6 +10,12 @@ description: |-
|
||||||
|
|
||||||
Provides an Elastic Load Balancer resource.
|
Provides an Elastic Load Balancer resource.
|
||||||
|
|
||||||
|
~> **NOTE on ELB Instances and ELB Attachments:** Terraform currently
|
||||||
|
provides both a standalone [ELB Attachment resource](elb_attachment.html)
|
||||||
|
(describing an instance attached to an ELB), and an ELB resource with
|
||||||
|
`instances` defined in-line. At this time you cannot use an ELB with in-line
|
||||||
|
instaces in conjunction with a ELB Attachment resources. Doing so will cause a
|
||||||
|
conflict and will overwrite attachments.
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
---
|
||||||
|
layout: "aws"
|
||||||
|
page_title: "AWS: aws_elb_attachment"
|
||||||
|
sidebar_current: "docs-aws-resource-elb-attachment"
|
||||||
|
description: |-
|
||||||
|
Provides an Elastic Load Balancer Attachment resource.
|
||||||
|
---
|
||||||
|
|
||||||
|
# aws\_elb\_attachment
|
||||||
|
|
||||||
|
Provides an Elastic Load Balancer Attachment resource.
|
||||||
|
|
||||||
|
~> **NOTE on ELB Instances and ELB Attachments:** Terraform currently provides
|
||||||
|
both a standalone ELB Attachment resource (describing an instance attached to
|
||||||
|
an ELB), and an [Elastic Load Balancer resource](elb.html) with
|
||||||
|
`instances` defined in-line. At this time you cannot use an ELB with in-line
|
||||||
|
instaces in conjunction with an ELB Attachment resource. Doing so will cause a
|
||||||
|
conflict and will overwrite attachments.
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
# Create a new load balancer attachment
|
||||||
|
resource "aws_elb_attachment" "baz" {
|
||||||
|
elb = "${aws_elb.bar.id}"
|
||||||
|
instance = ["${aws_instance.foo.id}"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `elb` - (Required) The name of the ELB.
|
||||||
|
* `instance` - (Required) Instance ID to place in the ELB pool.
|
Loading…
Reference in New Issue