Merge pull request #3756 from hashicorp/pr-3708
providers/aws: Add `access_logs` to ELB resource [GH-3756]
This commit is contained in:
commit
caa0baaf87
|
@ -107,6 +107,29 @@ func resourceAwsElb() *schema.Resource {
|
||||||
Default: 300,
|
Default: 300,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"access_logs": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Elem: &schema.Resource{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"interval": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Optional: true,
|
||||||
|
Default: 60,
|
||||||
|
},
|
||||||
|
"bucket": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"bucket_prefix": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Set: resourceAwsElbAccessLogsHash,
|
||||||
|
},
|
||||||
|
|
||||||
"listener": &schema.Schema{
|
"listener": &schema.Schema{
|
||||||
Type: schema.TypeSet,
|
Type: schema.TypeSet,
|
||||||
Required: true,
|
Required: true,
|
||||||
|
@ -323,6 +346,11 @@ func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout)
|
d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout)
|
||||||
d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled)
|
d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled)
|
||||||
d.Set("connection_draining_timeout", lbAttrs.ConnectionDraining.Timeout)
|
d.Set("connection_draining_timeout", lbAttrs.ConnectionDraining.Timeout)
|
||||||
|
if lbAttrs.AccessLog != nil {
|
||||||
|
if err := d.Set("access_logs", flattenAccessLog(lbAttrs.AccessLog)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{
|
resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{
|
||||||
LoadBalancerNames: []*string{lb.LoadBalancerName},
|
LoadBalancerNames: []*string{lb.LoadBalancerName},
|
||||||
|
@ -423,7 +451,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
d.SetPartial("instances")
|
d.SetPartial("instances")
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("cross_zone_load_balancing") || d.HasChange("idle_timeout") {
|
if d.HasChange("cross_zone_load_balancing") || d.HasChange("idle_timeout") || d.HasChange("access_logs") {
|
||||||
attrs := elb.ModifyLoadBalancerAttributesInput{
|
attrs := elb.ModifyLoadBalancerAttributesInput{
|
||||||
LoadBalancerName: aws.String(d.Get("name").(string)),
|
LoadBalancerName: aws.String(d.Get("name").(string)),
|
||||||
LoadBalancerAttributes: &elb.LoadBalancerAttributes{
|
LoadBalancerAttributes: &elb.LoadBalancerAttributes{
|
||||||
|
@ -436,6 +464,30 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logs := d.Get("access_logs").(*schema.Set).List()
|
||||||
|
if len(logs) > 1 {
|
||||||
|
return fmt.Errorf("Only one access logs config per ELB is supported")
|
||||||
|
} else if len(logs) == 1 {
|
||||||
|
log := logs[0].(map[string]interface{})
|
||||||
|
accessLog := &elb.AccessLog{
|
||||||
|
Enabled: aws.Bool(true),
|
||||||
|
EmitInterval: aws.Int64(int64(log["interval"].(int))),
|
||||||
|
S3BucketName: aws.String(log["bucket"].(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if log["bucket_prefix"] != "" {
|
||||||
|
accessLog.S3BucketPrefix = aws.String(log["bucket_prefix"].(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs.LoadBalancerAttributes.AccessLog = accessLog
|
||||||
|
} else if len(logs) == 0 {
|
||||||
|
// disable access logs
|
||||||
|
attrs.LoadBalancerAttributes.AccessLog = &elb.AccessLog{
|
||||||
|
Enabled: aws.Bool(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] ELB Modify Load Balancer Attributes Request: %#v", attrs)
|
||||||
_, err := elbconn.ModifyLoadBalancerAttributes(&attrs)
|
_, err := elbconn.ModifyLoadBalancerAttributes(&attrs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failure configuring ELB attributes: %s", err)
|
return fmt.Errorf("Failure configuring ELB attributes: %s", err)
|
||||||
|
@ -568,6 +620,19 @@ func resourceAwsElbHealthCheckHash(v interface{}) int {
|
||||||
return hashcode.String(buf.String())
|
return hashcode.String(buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resourceAwsElbAccessLogsHash(v interface{}) int {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
m := v.(map[string]interface{})
|
||||||
|
buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int)))
|
||||||
|
buf.WriteString(fmt.Sprintf("%s-",
|
||||||
|
strings.ToLower(m["bucket"].(string))))
|
||||||
|
if v, ok := m["bucket_prefix"]; ok {
|
||||||
|
buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(v.(string))))
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashcode.String(buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
func resourceAwsElbListenerHash(v interface{}) int {
|
func resourceAwsElbListenerHash(v interface{}) int {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
m := v.(map[string]interface{})
|
m := v.(map[string]interface{})
|
||||||
|
|
|
@ -75,6 +75,52 @@ func TestAccAWSELB_fullCharacterRange(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccAWSELB_AccessLogs(t *testing.T) {
|
||||||
|
var conf elb.LoadBalancerDescription
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSELBDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSELBAccessLogs,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSELBExists("aws_elb.foo", &conf),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_elb.foo", "name", "FoobarTerraform-test123"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSELBAccessLogsOn,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSELBExists("aws_elb.foo", &conf),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_elb.foo", "name", "FoobarTerraform-test123"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_elb.foo", "access_logs.#", "1"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_elb.foo", "access_logs.1713209538.bucket", "terraform-access-logs-bucket"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_elb.foo", "access_logs.1713209538.interval", "5"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSELBAccessLogs,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSELBExists("aws_elb.foo", &conf),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_elb.foo", "name", "FoobarTerraform-test123"),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"aws_elb.foo", "access_logs.#", "0"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccAWSELB_generatedName(t *testing.T) {
|
func TestAccAWSELB_generatedName(t *testing.T) {
|
||||||
var conf elb.LoadBalancerDescription
|
var conf elb.LoadBalancerDescription
|
||||||
generatedNameRegexp := regexp.MustCompile("^tf-lb-")
|
generatedNameRegexp := regexp.MustCompile("^tf-lb-")
|
||||||
|
@ -659,6 +705,64 @@ resource "aws_elb" "foo" {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const testAccAWSELBAccessLogs = `
|
||||||
|
resource "aws_elb" "foo" {
|
||||||
|
name = "FoobarTerraform-test123"
|
||||||
|
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
|
||||||
|
|
||||||
|
listener {
|
||||||
|
instance_port = 8000
|
||||||
|
instance_protocol = "http"
|
||||||
|
lb_port = 80
|
||||||
|
lb_protocol = "http"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const testAccAWSELBAccessLogsOn = `
|
||||||
|
# an S3 bucket configured for Access logs
|
||||||
|
# The 797873946194 is the AWS ID for us-west-2, so this test
|
||||||
|
# must be ran in us-west-2
|
||||||
|
resource "aws_s3_bucket" "acceslogs_bucket" {
|
||||||
|
bucket = "terraform-access-logs-bucket"
|
||||||
|
acl = "private"
|
||||||
|
force_destroy = true
|
||||||
|
policy = <<EOF
|
||||||
|
{
|
||||||
|
"Id": "Policy1446577137248",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Action": "s3:PutObject",
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": {
|
||||||
|
"AWS": "arn:aws:iam::797873946194:root"
|
||||||
|
},
|
||||||
|
"Resource": "arn:aws:s3:::terraform-access-logs-bucket/*",
|
||||||
|
"Sid": "Stmt1446575236270"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Version": "2012-10-17"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_elb" "foo" {
|
||||||
|
name = "FoobarTerraform-test123"
|
||||||
|
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
|
||||||
|
|
||||||
|
listener {
|
||||||
|
instance_port = 8000
|
||||||
|
instance_protocol = "http"
|
||||||
|
lb_port = 80
|
||||||
|
lb_protocol = "http"
|
||||||
|
}
|
||||||
|
|
||||||
|
access_logs {
|
||||||
|
interval = 5
|
||||||
|
bucket = "${aws_s3_bucket.acceslogs_bucket.bucket}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const testAccAWSELBGeneratedName = `
|
const testAccAWSELBGeneratedName = `
|
||||||
resource "aws_elb" "foo" {
|
resource "aws_elb" "foo" {
|
||||||
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
|
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
|
||||||
|
|
|
@ -238,6 +238,33 @@ func expandElastiCacheParameters(configured []interface{}) ([]*elasticache.Param
|
||||||
return parameters, nil
|
return parameters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flattens an access log into something that flatmap.Flatten() can handle
|
||||||
|
func flattenAccessLog(log *elb.AccessLog) []map[string]interface{} {
|
||||||
|
result := make([]map[string]interface{}, 0, 1)
|
||||||
|
|
||||||
|
if log != nil {
|
||||||
|
r := make(map[string]interface{})
|
||||||
|
// enabled is the only value we can rely on to not be nil
|
||||||
|
r["enabled"] = *log.Enabled
|
||||||
|
|
||||||
|
if log.S3BucketName != nil {
|
||||||
|
r["bucket"] = *log.S3BucketName
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.S3BucketPrefix != nil {
|
||||||
|
r["bucket_prefix"] = *log.S3BucketPrefix
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.EmitInterval != nil {
|
||||||
|
r["interval"] = *log.EmitInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// Flattens a health check into something that flatmap.Flatten()
|
// Flattens a health check into something that flatmap.Flatten()
|
||||||
// can handle
|
// can handle
|
||||||
func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} {
|
func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} {
|
||||||
|
|
|
@ -18,6 +18,12 @@ resource "aws_elb" "bar" {
|
||||||
name = "foobar-terraform-elb"
|
name = "foobar-terraform-elb"
|
||||||
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
|
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
|
||||||
|
|
||||||
|
access_logs {
|
||||||
|
bucket = "foo"
|
||||||
|
bucket_prefix = "bar"
|
||||||
|
interval = 60
|
||||||
|
}
|
||||||
|
|
||||||
listener {
|
listener {
|
||||||
instance_port = 8000
|
instance_port = 8000
|
||||||
instance_protocol = "http"
|
instance_protocol = "http"
|
||||||
|
@ -58,6 +64,7 @@ resource "aws_elb" "bar" {
|
||||||
The following arguments are supported:
|
The following arguments are supported:
|
||||||
|
|
||||||
* `name` - (Optional) The name of the ELB. By default generated by terraform.
|
* `name` - (Optional) The name of the ELB. By default generated by terraform.
|
||||||
|
* `access_logs` - (Optional) An Access Logs block. Access Logs documented below.
|
||||||
* `availability_zones` - (Required for an EC2-classic ELB) The AZ's to serve traffic in.
|
* `availability_zones` - (Required for an EC2-classic ELB) The AZ's to serve traffic in.
|
||||||
* `security_groups` - (Optional) A list of security group IDs to assign to the ELB.
|
* `security_groups` - (Optional) A list of security group IDs to assign to the ELB.
|
||||||
* `subnets` - (Required for a VPC ELB) A list of subnet IDs to attach to the ELB.
|
* `subnets` - (Required for a VPC ELB) A list of subnet IDs to attach to the ELB.
|
||||||
|
@ -74,6 +81,12 @@ The following arguments are supported:
|
||||||
Exactly one of `availability_zones` or `subnets` must be specified: this
|
Exactly one of `availability_zones` or `subnets` must be specified: this
|
||||||
determines if the ELB exists in a VPC or in EC2-classic.
|
determines if the ELB exists in a VPC or in EC2-classic.
|
||||||
|
|
||||||
|
Access Logs support the following:
|
||||||
|
|
||||||
|
* `bucket` - (Required) The S3 bucket name to store the logs in.
|
||||||
|
* `bucket_prefix` - (Optional) The S3 bucket prefix. Logs are stored in the root if not configured.
|
||||||
|
* `interval` - (Optional) The publishing interval in minutes. Default: 60 minutes.
|
||||||
|
|
||||||
Listeners support the following:
|
Listeners support the following:
|
||||||
|
|
||||||
* `instance_port` - (Required) The port on the instance to route to
|
* `instance_port` - (Required) The port on the instance to route to
|
||||||
|
|
Loading…
Reference in New Issue