-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2384 from hashicorp/f-aws-flow-logs
provider/aws: Add FlowLog resource
- Loading branch information
Showing
5 changed files
with
457 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
` |
Oops, something went wrong.