Skip to content

Commit

Permalink
Merge pull request #2384 from hashicorp/f-aws-flow-logs
Browse files Browse the repository at this point in the history
provider/aws: Add FlowLog resource
  • Loading branch information
catsby committed Jun 23, 2015
2 parents cf40c6e + 8e23607 commit 24c4c55
Show file tree
Hide file tree
Showing 5 changed files with 457 additions and 1 deletion.
3 changes: 2 additions & 1 deletion builtin/providers/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func Provider() terraform.ResourceProvider {
"aws_db_parameter_group": resourceAwsDbParameterGroup(),
"aws_db_security_group": resourceAwsDbSecurityGroup(),
"aws_db_subnet_group": resourceAwsDbSubnetGroup(),
"aws_dynamodb_table": resourceAwsDynamoDbTable(),
"aws_dynamodb_table": resourceAwsDynamoDbTable(),
"aws_ebs_volume": resourceAwsEbsVolume(),
"aws_ecs_cluster": resourceAwsEcsCluster(),
"aws_ecs_service": resourceAwsEcsService(),
Expand All @@ -103,6 +103,7 @@ func Provider() terraform.ResourceProvider {
"aws_elasticache_security_group": resourceAwsElasticacheSecurityGroup(),
"aws_elasticache_subnet_group": resourceAwsElasticacheSubnetGroup(),
"aws_elb": resourceAwsElb(),
"aws_flow_log": resourceAwsFlowLog(),
"aws_iam_access_key": resourceAwsIamAccessKey(),
"aws_iam_group_policy": resourceAwsIamGroupPolicy(),
"aws_iam_group": resourceAwsIamGroup(),
Expand Down
155 changes: 155 additions & 0 deletions builtin/providers/aws/resource_aws_flow_log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package aws

import (
"fmt"
"log"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsFlowLog() *schema.Resource {
return &schema.Resource{
Create: resourceAwsLogFlowCreate,
Read: resourceAwsLogFlowRead,
Delete: resourceAwsLogFlowDelete,

Schema: map[string]*schema.Schema{
"iam_role_arn": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"log_group_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"vpc_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"subnet_id", "eni_id"},
},

"subnet_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"eni_id", "vpc_id"},
},

"eni_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"subnet_id", "vpc_id"},
},

"traffic_type": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
}
}

func resourceAwsLogFlowCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

types := []struct {
ID string
Type string
}{
{ID: d.Get("vpc_id").(string), Type: "VPC"},
{ID: d.Get("subnet_id").(string), Type: "Subnet"},
{ID: d.Get("eni_id").(string), Type: "NetworkInterface"},
}

var resourceId string
var resourceType string
for _, t := range types {
if t.ID != "" {
resourceId = t.ID
resourceType = t.Type
break
}
}

if resourceId == "" || resourceType == "" {
return fmt.Errorf("Error: Flow Logs require either a VPC, Subnet, or ENI ID")
}

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)),
}

log.Printf(
"[DEBUG] Flow Log Create configuration: %s", awsutil.StringValue(opts))
resp, err := conn.CreateFlowLogs(opts)
if err != nil {
return fmt.Errorf("Error creating Flow Log for (%s), error: %s", resourceId, err)
}

if len(resp.FlowLogIDs) > 1 {
return fmt.Errorf("Error: multiple Flow Logs created for (%s)", resourceId)
}

d.SetId(*resp.FlowLogIDs[0])

return resourceAwsLogFlowRead(d, meta)
}

func resourceAwsLogFlowRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

opts := &ec2.DescribeFlowLogsInput{
FlowLogIDs: []*string{aws.String(d.Id())},
}

resp, err := conn.DescribeFlowLogs(opts)
if err != nil {
log.Printf("[WARN] Error describing Flow Logs for id (%s)", d.Id())
d.SetId("")
return nil
}

if len(resp.FlowLogs) == 0 {
log.Printf("[WARN] No Flow Logs found for id (%s)", d.Id())
d.SetId("")
return nil
}

fl := resp.FlowLogs[0]

d.Set("traffic_type", fl.TrafficType)
d.Set("log_group_name", fl.LogGroupName)
d.Set("iam_role_arn", fl.DeliverLogsPermissionARN)

return nil
}

func resourceAwsLogFlowDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

log.Printf(
"[DEBUG] Flow Log Destroy: %s", d.Id())
_, err := conn.DeleteFlowLogs(&ec2.DeleteFlowLogsInput{
FlowLogIDs: []*string{aws.String(d.Id())},
})

if err != nil {
return fmt.Errorf("[WARN] Error deleting Flow Log with ID (%s), error: %s", d.Id(), err)
}

return nil
}
212 changes: 212 additions & 0 deletions builtin/providers/aws/resource_aws_flow_log_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
package aws

import (
"fmt"
"os"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccFlowLog_basic(t *testing.T) {
var flowLog ec2.FlowLog
lgn := os.Getenv("LOG_GROUP_NAME")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckFlowLogDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(testAccFlowLogConfig_basic, lgn),
Check: resource.ComposeTestCheckFunc(
testAccCheckFlowLogExists("aws_flow_log.test_flow_log", &flowLog),
testAccCheckAWSFlowLogAttributes(&flowLog),
),
},
},
})
}

func TestAccFlowLog_subnet(t *testing.T) {
var flowLog ec2.FlowLog
lgn := os.Getenv("LOG_GROUP_NAME")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckFlowLogDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(testAccFlowLogConfig_subnet, lgn),
Check: resource.ComposeTestCheckFunc(
testAccCheckFlowLogExists("aws_flow_log.test_flow_log_subnet", &flowLog),
testAccCheckAWSFlowLogAttributes(&flowLog),
),
},
},
})
}

func testAccCheckFlowLogExists(n string, flowLog *ec2.FlowLog) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No Flow Log ID is set")
}

conn := testAccProvider.Meta().(*AWSClient).ec2conn
describeOpts := &ec2.DescribeFlowLogsInput{
FlowLogIDs: []*string{aws.String(rs.Primary.ID)},
}
resp, err := conn.DescribeFlowLogs(describeOpts)
if err != nil {
return err
}

if len(resp.FlowLogs) > 0 {
*flowLog = *resp.FlowLogs[0]
return nil
}
return fmt.Errorf("No Flow Logs found for id (%s)", rs.Primary.ID)
}
}

func testAccCheckAWSFlowLogAttributes(flowLog *ec2.FlowLog) resource.TestCheckFunc {
return func(s *terraform.State) error {
if flowLog.FlowLogStatus != nil && *flowLog.FlowLogStatus == "ACTIVE" {
return nil
}
if flowLog.FlowLogStatus == nil {
return fmt.Errorf("Flow Log status is not ACTIVE, is nil")
} else {
return fmt.Errorf("Flow Log status is not ACTIVE, got: %s", *flowLog.FlowLogStatus)
}
}
}

func testAccCheckFlowLogDestroy(s *terraform.State) error {
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_flow_log" {
continue
}

return nil
}

return nil
}

var testAccFlowLogConfig_basic = `
resource "aws_vpc" "default" {
cidr_block = "10.0.0.0/16"
tags {
Name = "tf-flow-log-test"
}
}
resource "aws_subnet" "test_subnet" {
vpc_id = "${aws_vpc.default.id}"
cidr_block = "10.0.1.0/24"
tags {
Name = "tf-flow-test"
}
}
resource "aws_iam_role" "test_role" {
name = "test_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"ec2.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
}
EOF
}
resource "aws_flow_log" "test_flow_log" {
# log_group_name needs to exist before hand
# until we have a CloudWatch Log Group Resource
log_group_name = "tf-test-log-group"
iam_role_arn = "${aws_iam_role.test_role.arn}"
vpc_id = "${aws_vpc.default.id}"
traffic_type = "ALL"
}
resource "aws_flow_log" "test_flow_log_subnet" {
# log_group_name needs to exist before hand
# until we have a CloudWatch Log Group Resource
log_group_name = "%s"
iam_role_arn = "${aws_iam_role.test_role.arn}"
subnet_id = "${aws_subnet.test_subnet.id}"
traffic_type = "ALL"
}
`

var testAccFlowLogConfig_subnet = `
resource "aws_vpc" "default" {
cidr_block = "10.0.0.0/16"
tags {
Name = "tf-flow-log-test"
}
}
resource "aws_subnet" "test_subnet" {
vpc_id = "${aws_vpc.default.id}"
cidr_block = "10.0.1.0/24"
tags {
Name = "tf-flow-test"
}
}
resource "aws_iam_role" "test_role" {
name = "test_role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"ec2.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
}
EOF
}
resource "aws_flow_log" "test_flow_log_subnet" {
# log_group_name needs to exist before hand
# until we have a CloudWatch Log Group Resource
log_group_name = "%s"
iam_role_arn = "${aws_iam_role.test_role.arn}"
subnet_id = "${aws_subnet.test_subnet.id}"
traffic_type = "ALL"
}
`
Loading

0 comments on commit 24c4c55

Please sign in to comment.