diff --git a/aws/resource_aws_flow_log.go b/aws/resource_aws_flow_log.go index e67b5b418f0..8414d9a4765 100644 --- a/aws/resource_aws_flow_log.go +++ b/aws/resource_aws_flow_log.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" ) func resourceAwsFlowLog() *schema.Resource { @@ -22,14 +23,37 @@ func resourceAwsFlowLog() *schema.Resource { Schema: map[string]*schema.Schema{ "iam_role_arn": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, }, - "log_group_name": { + "log_destination": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"log_group_name"}, + ValidateFunc: validateArn, + }, + + "log_destination_type": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, + Default: ec2.LogDestinationTypeCloudWatchLogs, + ValidateFunc: validation.StringInSlice([]string{ + ec2.LogDestinationTypeCloudWatchLogs, + ec2.LogDestinationTypeS3, + }, false), + }, + + "log_group_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"log_destination"}, + Deprecated: "use 'log_destination' argument instead", }, "vpc_id": { @@ -89,11 +113,22 @@ func resourceAwsLogFlowCreate(d *schema.ResourceData, meta interface{}) error { } opts := &ec2.CreateFlowLogsInput{ - DeliverLogsPermissionArn: aws.String(d.Get("iam_role_arn").(string)), - LogGroupName: aws.String(d.Get("log_group_name").(string)), - ResourceIds: []*string{aws.String(resourceId)}, - ResourceType: aws.String(resourceType), - TrafficType: aws.String(d.Get("traffic_type").(string)), + LogDestinationType: aws.String(d.Get("log_destination_type").(string)), + ResourceIds: []*string{aws.String(resourceId)}, + ResourceType: aws.String(resourceType), + TrafficType: aws.String(d.Get("traffic_type").(string)), + } + + if v, ok := d.GetOk("iam_role_arn"); ok && v != "" { + opts.DeliverLogsPermissionArn = aws.String(v.(string)) + } + + if v, ok := d.GetOk("log_destination"); ok && v != "" { + opts.LogDestination = aws.String(v.(string)) + } + + if v, ok := d.GetOk("log_group_name"); ok && v != "" { + opts.LogGroupName = aws.String(v.(string)) } log.Printf( @@ -134,6 +169,8 @@ func resourceAwsLogFlowRead(d *schema.ResourceData, meta interface{}) error { fl := resp.FlowLogs[0] d.Set("traffic_type", fl.TrafficType) + d.Set("log_destination", fl.LogDestination) + d.Set("log_destination_type", fl.LogDestinationType) d.Set("log_group_name", fl.LogGroupName) d.Set("iam_role_arn", fl.DeliverLogsPermissionArn) diff --git a/aws/resource_aws_flow_log_test.go b/aws/resource_aws_flow_log_test.go index c6f1da505f1..fc6a9b880f7 100644 --- a/aws/resource_aws_flow_log_test.go +++ b/aws/resource_aws_flow_log_test.go @@ -11,10 +11,13 @@ import ( "github.com/hashicorp/terraform/terraform" ) -func TestAccAWSFlowLog_importBasic(t *testing.T) { - resourceName := "aws_flow_log.test_flow_log" - - rInt := acctest.RandInt() +func TestAccAWSFlowLog_VPCID(t *testing.T) { + var flowLog ec2.FlowLog + cloudwatchLogGroupResourceName := "aws_cloudwatch_log_group.test" + iamRoleResourceName := "aws_iam_role.test" + resourceName := "aws_flow_log.test" + vpcResourceName := "aws_vpc.test" + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -22,58 +25,122 @@ func TestAccAWSFlowLog_importBasic(t *testing.T) { CheckDestroy: testAccCheckFlowLogDestroy, Steps: []resource.TestStep{ { - Config: testAccFlowLogConfig_basic(rInt), + Config: testAccFlowLogConfig_VPCID(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFlowLogExists(resourceName, &flowLog), + testAccCheckAWSFlowLogAttributes(&flowLog), + resource.TestCheckResourceAttrPair(resourceName, "iam_role_arn", iamRoleResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination", ""), + resource.TestCheckResourceAttr(resourceName, "log_destination_type", "cloud-watch-logs"), + resource.TestCheckResourceAttrPair(resourceName, "log_group_name", cloudwatchLogGroupResourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "traffic_type", "ALL"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_id", vpcResourceName, "id"), + ), }, - { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, + { + Config: testAccFlowLogConfig_LogDestinationType_CloudWatchLogs(rName), + ExpectNonEmptyPlan: false, + }, }, }) } -func TestAccAWSFlowLog_basic(t *testing.T) { +func TestAccAWSFlowLog_SubnetID(t *testing.T) { var flowLog ec2.FlowLog + cloudwatchLogGroupResourceName := "aws_cloudwatch_log_group.test" + iamRoleResourceName := "aws_iam_role.test" + resourceName := "aws_flow_log.test" + subnetResourceName := "aws_subnet.test" + rName := acctest.RandomWithPrefix("tf-acc-test") - rInt := acctest.RandInt() - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - IDRefreshName: "aws_flow_log.test_flow_log", - Providers: testAccProviders, - CheckDestroy: testAccCheckFlowLogDestroy, + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFlowLogDestroy, Steps: []resource.TestStep{ { - Config: testAccFlowLogConfig_basic(rInt), + Config: testAccFlowLogConfig_SubnetID(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFlowLogExists("aws_flow_log.test_flow_log", &flowLog), + testAccCheckFlowLogExists(resourceName, &flowLog), testAccCheckAWSFlowLogAttributes(&flowLog), + resource.TestCheckResourceAttrPair(resourceName, "iam_role_arn", iamRoleResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination", ""), + resource.TestCheckResourceAttr(resourceName, "log_destination_type", "cloud-watch-logs"), + resource.TestCheckResourceAttrPair(resourceName, "log_group_name", cloudwatchLogGroupResourceName, "name"), + resource.TestCheckResourceAttrPair(resourceName, "subnet_id", subnetResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "traffic_type", "ALL"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } -func TestAccAWSFlowLog_subnet(t *testing.T) { +func TestAccAWSFlowLog_LogDestinationType_CloudWatchLogs(t *testing.T) { var flowLog ec2.FlowLog + cloudwatchLogGroupResourceName := "aws_cloudwatch_log_group.test" + resourceName := "aws_flow_log.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFlowLogDestroy, + Steps: []resource.TestStep{ + { + Config: testAccFlowLogConfig_LogDestinationType_CloudWatchLogs(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFlowLogExists(resourceName, &flowLog), + testAccCheckAWSFlowLogAttributes(&flowLog), + resource.TestCheckResourceAttrPair(resourceName, "log_destination", cloudwatchLogGroupResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_type", "cloud-watch-logs"), + resource.TestCheckResourceAttr(resourceName, "log_group_name", fmt.Sprintf("%s:*", rName)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} - rInt := acctest.RandInt() +func TestAccAWSFlowLog_LogDestinationType_S3(t *testing.T) { + var flowLog ec2.FlowLog + s3ResourceName := "aws_s3_bucket.test" + resourceName := "aws_flow_log.test" + rName := acctest.RandomWithPrefix("tf-acc-test") - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - IDRefreshName: "aws_flow_log.test_flow_log_subnet", - Providers: testAccProviders, - CheckDestroy: testAccCheckFlowLogDestroy, + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFlowLogDestroy, Steps: []resource.TestStep{ { - Config: testAccFlowLogConfig_subnet(rInt), + Config: testAccFlowLogConfig_LogDestinationType_S3(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFlowLogExists("aws_flow_log.test_flow_log_subnet", &flowLog), + testAccCheckFlowLogExists(resourceName, &flowLog), testAccCheckAWSFlowLogAttributes(&flowLog), + resource.TestCheckResourceAttrPair(resourceName, "log_destination", s3ResourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "log_destination_type", "s3"), + resource.TestCheckResourceAttr(resourceName, "log_group_name", ""), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -131,27 +198,19 @@ func testAccCheckFlowLogDestroy(s *terraform.State) error { return nil } -func testAccFlowLogConfig_basic(rInt int) string { +func testAccFlowLogConfig_LogDestinationType_CloudWatchLogs(rName string) string { return fmt.Sprintf(` -resource "aws_vpc" "default" { - cidr_block = "10.0.0.0/16" - tags { - Name = "terraform-testacc-flow-log-basic" - } -} - -resource "aws_subnet" "test_subnet" { - vpc_id = "${aws_vpc.default.id}" - cidr_block = "10.0.1.0/24" +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" - tags { - Name = "tf-acc-flow-log-basic" - } + tags { + Name = %q + } } -resource "aws_iam_role" "test_role" { - name = "tf_test_flow_log_basic_%d" - assume_role_policy = < **NOTE:** One of `eni_id`, `subnet_id`, or `vpc_id` must be specified. + The following arguments are supported: -* `log_group_name` - (Required) The name of the CloudWatch log group -* `iam_role_arn` - (Required) The ARN for the IAM role that's used to post flow - logs to a CloudWatch Logs log group -* `vpc_id` - (Optional) VPC ID to attach to -* `subnet_id` - (Optional) Subnet ID to attach to +* `traffic_type` - (Required) The type of traffic to capture. Valid values: `ACCEPT`,`REJECT`, `ALL`. * `eni_id` - (Optional) Elastic Network Interface ID to attach to -* `traffic_type` - (Required) The type of traffic to capture. Valid values: - `ACCEPT`,`REJECT`, `ALL` +* `iam_role_arn` - (Optional) The ARN for the IAM role that's used to post flow logs to a CloudWatch Logs log group +* `log_destination_type` - (Optional) The type of the logging destination. Valid values: `cloud-watch-logs`, `s3`. Default: `cloud-watch-logs`. +* `log_destination` - (Optional) The ARN of the logging destination. +* `log_group_name` - (Optional) *Deprecated:* Use `log_destination` instead. The name of the CloudWatch log group. +* `subnet_id` - (Optional) Subnet ID to attach to +* `vpc_id` - (Optional) VPC ID to attach to ## Attributes Reference