diff --git a/builtin/providers/aws/resource_aws_elb.go b/builtin/providers/aws/resource_aws_elb.go index 91b4882cb..5ff3b3b28 100644 --- a/builtin/providers/aws/resource_aws_elb.go +++ b/builtin/providers/aws/resource_aws_elb.go @@ -107,6 +107,29 @@ func resourceAwsElb() *schema.Resource { 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{ Type: schema.TypeSet, Required: true, @@ -323,6 +346,11 @@ func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error { d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout) d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled) 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{ LoadBalancerNames: []*string{lb.LoadBalancerName}, @@ -423,7 +451,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error { 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{ LoadBalancerName: aws.String(d.Get("name").(string)), 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) if err != nil { return fmt.Errorf("Failure configuring ELB attributes: %s", err) @@ -568,6 +620,19 @@ func resourceAwsElbHealthCheckHash(v interface{}) int { 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 { var buf bytes.Buffer m := v.(map[string]interface{}) diff --git a/builtin/providers/aws/resource_aws_elb_test.go b/builtin/providers/aws/resource_aws_elb_test.go index 9d010d846..6dad03e56 100644 --- a/builtin/providers/aws/resource_aws_elb_test.go +++ b/builtin/providers/aws/resource_aws_elb_test.go @@ -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) { var conf elb.LoadBalancerDescription 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 = <