Skip to content

Instantly share code, notes, and snippets.

@davidski
Last active January 5, 2024 12:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save davidski/788afdb2a79d77edfb5fea65b50369e7 to your computer and use it in GitHub Desktop.
Save davidski/788afdb2a79d77edfb5fea65b50369e7 to your computer and use it in GitHub Desktop.
Step Function Definition for AMI Security Validator
# reusable policy for the roles which Lambda has permission to assume
data "aws_iam_policy_document" "lambda-assume-role-policy" {
statement {
actions = ["sts:AssumeRole"]
principals = {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
/*
----------------------
| POLICY DEFINITIONS |
----------------------
*/
data "aws_iam_policy_document" "ec2_tagger" {
statement {
sid = "1"
actions = [
"ec2:CreateTags",
]
resources = ["*"]
}
}
resource "aws_iam_policy" "ec2_tagger" {
name_prefix = "ec2_tagger"
description = "Permits modification of EC2 tags"
policy = "${data.aws_iam_policy_document.ec2_tagger.json}"
}
data "aws_iam_policy_document" "ec2_spot_instance_read_only" {
statement {
sid = "1"
actions = [
"ec2:DescribeSpotInstanceRequests",
"ec2:DescribeInstanceStatus",
]
resources = ["*"]
}
}
resource "aws_iam_policy" "ec2_spot_instance_read_only" {
name_prefix = "ec2_spot_instance_read_only"
description = "Permits read of EC2 spot instance requests"
policy = "${data.aws_iam_policy_document.ec2_spot_instance_read_only.json}"
}
data "aws_iam_policy_document" "ec2_terminate_instance" {
statement {
sid = "1"
actions = [
"ec2:TerminateInstances",
]
resources = [
"*",
]
}
}
resource "aws_iam_policy" "ec2_terminate_instance" {
name_prefix = "ec2_terminate_instance"
description = "Permits the termination of EC2 instances"
policy = "${data.aws_iam_policy_document.ec2_terminate_instance.json}"
}
data "aws_iam_policy_document" "ec2_request_spot_instances" {
statement {
sid = "1"
actions = [
"ec2:RequestSpotInstances",
]
resources = [
"*",
]
}
statement {
actions = [
"iam:PassRole",
]
resources = [
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/*",
]
}
}
resource "aws_iam_policy" "ec2_request_spot_instances" {
name_prefix = "ec2_request_spot_instances"
description = "Permits requesting new EC2 spot instances"
policy = "${data.aws_iam_policy_document.ec2_request_spot_instances.json}"
}
data "aws_iam_policy_document" "start_step_functions" {
statement {
sid = "1"
actions = [
"stepfunctions:StartExecution",
]
resources = ["*"]
}
}
resource "aws_iam_policy" "start_step_functions" {
name_prefix = "start_step_functions"
description = "Permits launching any defined AWS Step Functions"
policy = "${data.aws_iam_policy_document.start_step_functions.json}"
}
/*
--------------------
| ROLE DEFINITIONS |
--------------------
*/
# launch-instance
resource "aws_iam_role" "launch_instance" {
name_prefix = "launch_instance"
assume_role_policy = "${data.aws_iam_policy_document.lambda-assume-role-policy.json}"
}
resource "aws_iam_role_policy_attachment" "launch_instance1" {
role = "${aws_iam_role.launch_instance.id}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy_attachment" "launch_instance2" {
role = "${aws_iam_role.launch_instance.id}"
policy_arn = "${aws_iam_policy.ec2_tagger.arn}"
}
resource "aws_iam_role_policy_attachment" "launch_instance3" {
role = "${aws_iam_role.launch_instance.id}"
policy_arn = "${aws_iam_policy.ec2_request_spot_instances.arn}"
}
output "launch_instance_role_arn" {
value = "${aws_iam_role.launch_instance.arn}"
}
# check-instance-ready
resource "aws_iam_role" "check_instance_ready" {
name_prefix = "check_instance_ready"
assume_role_policy = "${data.aws_iam_policy_document.lambda-assume-role-policy.json}"
}
resource "aws_iam_role_policy_attachment" "check_instance_ready1" {
role = "${aws_iam_role.check_instance_ready.id}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy_attachment" "check_instance_ready2" {
role = "${aws_iam_role.check_instance_ready.id}"
policy_arn = "${aws_iam_policy.ec2_spot_instance_read_only.arn}"
}
output "check_instance_ready_role_arn" {
value = "${aws_iam_role.check_instance_ready.arn}"
}
# tag-ec2-resource
resource "aws_iam_role" "tag_ec2_resource" {
name_prefix = "tag_ec2_resource"
assume_role_policy = "${data.aws_iam_policy_document.lambda-assume-role-policy.json}"
}
resource "aws_iam_role_policy_attachment" "tag_ec2_resource1" {
role = "${aws_iam_role.tag_ec2_resource.id}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy_attachment" "tag_ec2_resource2" {
role = "${aws_iam_role.tag_ec2_resource.id}"
policy_arn = "${aws_iam_policy.ec2_tagger.arn}"
}
output "tag_ec2_resource_role_arn" {
value = "${aws_iam_role.tag_ec2_resource.arn}"
}
# start-inspector-assessment-run
resource "aws_iam_role" "start_inspector_assessment_run" {
name_prefix = "start_inspector_assessment_run"
assume_role_policy = "${data.aws_iam_policy_document.lambda-assume-role-policy.json}"
}
resource "aws_iam_role_policy_attachment" "start_inspector_assessment_run1" {
role = "${aws_iam_role.start_inspector_assessment_run.id}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy_attachment" "start_inspector_assessment_run2" {
role = "${aws_iam_role.start_inspector_assessment_run.id}"
policy_arn = "arn:aws:iam::aws:policy/AmazonInspectorFullAccess"
}
output "start_inspector_assessment_run_role_arn" {
value = "${aws_iam_role.start_inspector_assessment_run.arn}"
}
# check-inspector-assessment-run-complete
resource "aws_iam_role" "check_inspector_assessment_run_complete" {
name_prefix = "InspectorAssessmentRunsReadOnly"
assume_role_policy = "${data.aws_iam_policy_document.lambda-assume-role-policy.json}"
}
resource "aws_iam_role_policy_attachment" "check_inspector_assessment_run_complete" {
role = "${aws_iam_role.check_inspector_assessment_run_complete.id}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy_attachment" "check_inspector_assessment_run_complete2" {
role = "${aws_iam_role.check_inspector_assessment_run_complete.id}"
policy_arn = "arn:aws:iam::aws:policy/AmazonInspectorReadOnlyAccess"
}
output "check_inspector_assessment_run_complete_role_arn" {
value = "${aws_iam_role.check_inspector_assessment_run_complete.arn}"
}
# parse-inspector-assessment-run-findings
resource "aws_iam_role" "parse_inspector_assessment_run_findings" {
name_prefix = "InspectorFindingReadOnly"
assume_role_policy = "${data.aws_iam_policy_document.lambda-assume-role-policy.json}"
}
resource "aws_iam_role_policy_attachment" "parse_inspector_assessment_run_findings1" {
role = "${aws_iam_role.parse_inspector_assessment_run_findings.id}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy_attachment" "parse_inspector_assessment_run_findings2" {
role = "${aws_iam_role.parse_inspector_assessment_run_findings.id}"
policy_arn = "arn:aws:iam::aws:policy/AmazonInspectorReadOnlyAccess"
}
output "parse_inspector_assessment_run_findings_role_arn" {
value = "${aws_iam_role.parse_inspector_assessment_run_findings.arn}"
}
# terminate-instance
resource "aws_iam_role" "terminate_instance" {
name_prefix = "terminate_instance"
assume_role_policy = "${data.aws_iam_policy_document.lambda-assume-role-policy.json}"
}
resource "aws_iam_role_policy_attachment" "terminate_instance1" {
role = "${aws_iam_role.terminate_instance.id}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy_attachment" "terminate_instance2" {
role = "${aws_iam_role.terminate_instance.id}"
policy_arn = "${aws_iam_policy.ec2_terminate_instance.arn}"
}
output "terminate_instance_role_arn" {
value = "${aws_iam_role.terminate_instance.arn}"
}
# start-step-function
resource "aws_iam_role" "start_step_functions" {
name_prefix = "start_step_functions"
assume_role_policy = "${data.aws_iam_policy_document.lambda-assume-role-policy.json}"
}
resource "aws_iam_role_policy_attachment" "start_step_functions" {
role = "${aws_iam_role.start_step_functions.id}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaRole"
}
output "start_step_functions_role_arn" {
value = "${aws_iam_role.start_step_functions.arn}"
}
/*
--------------------
| LAMBDA FUNCTIONS |
--------------------
*/
resource "aws_lambda_function" "check_inspector_assessment_run_complete" {
s3_bucket = "${aws_s3_bucket.artifacts.id}"
s3_key = "lambdas/check-inspector-assessment-run-complete.zip"
function_name = "check_inspector_assessment_run_complete"
description = "Check an AWS Inspector Assessment ARN and return success if completed"
role = "${aws_iam_role.check_inspector_assessment_run_complete.arn}"
handler = "main.lambda_handler"
runtime = "python2.7"
}
resource "aws_lambda_function" "check_instance_ready" {
s3_bucket = "${aws_s3_bucket.artifacts.id}"
s3_key = "lambdas/check-instance-ready.zip"
function_name = "check_instance_ready"
description = "Check if a given instance id is in a READY state"
role = "${aws_iam_role.check_instance_ready.arn}"
handler = "main.lambda_handler"
runtime = "python2.7"
}
resource "aws_lambda_function" "launch_instance" {
s3_bucket = "${aws_s3_bucket.artifacts.id}"
s3_key = "lambdas/launch-instance.zip"
function_name = "launch_instance"
description = "Launch an instance of an AMI passed by id"
role = "${aws_iam_role.launch_instance.arn}"
handler = "main.lambda_handler"
runtime = "python2.7"
environment {
variables {
instance_profile = "arn:aws:iam::REDACTED:instance-profile/aws-packer-ec2"
security_group = "REDACTED"
instance_type = "c3.large"
}
}
}
resource "aws_lambda_function" "parse_inspector_assessment_run_findings" {
s3_bucket = "${aws_s3_bucket.artifacts.id}"
s3_key = "lambdas/parse-inspector-assessment-run-findings.zip"
function_name = "parse_inspector_assessment_run_findings"
description = "Review all the findings for a given assessment run and return a pass/fail result based on security ratings"
role = "${aws_iam_role.parse_inspector_assessment_run_findings.arn}"
handler = "main.lambda_handler"
runtime = "python2.7"
}
resource "aws_lambda_function" "start_inspector_assessment_run" {
s3_bucket = "${aws_s3_bucket.artifacts.id}"
s3_key = "lambdas/start-inspector-assessment-run.zip"
function_name = "start_inspector_assessment_run"
description = "Build and launch an AWS Inspector Assessment run"
role = "${aws_iam_role.start_inspector_assessment_run.arn}"
handler = "main.lambda_handler"
runtime = "python2.7"
}
resource "aws_lambda_function" "tag_ec2_resource" {
s3_bucket = "${aws_s3_bucket.artifacts.id}"
s3_key = "lambdas/tag-ec2-resource.zip"
function_name = "tag_ec2_resource"
description = "Apply a set of tags to a EC2 resource passed by id"
role = "${aws_iam_role.tag_ec2_resource.arn}"
handler = "main.lambda_handler"
runtime = "python2.7"
}
resource "aws_lambda_function" "terminate_instance" {
s3_bucket = "${aws_s3_bucket.artifacts.id}"
s3_key = "lambdas/terminate-instance.zip"
function_name = "terminate_instance"
description = "Terminate an EC2 instance by id"
role = "${aws_iam_role.terminate_instance.arn}"
handler = "main.lambda_handler"
runtime = "python2.7"
}
resource "aws_lambda_function" "start_step_function" {
s3_bucket = "${aws_s3_bucket.artifacts.id}"
s3_key = "lambdas/start-step-function.zip"
function_name = "start_step_function"
description = "Receive a CloudTrail event and pass it to an AWS step function"
role = "${aws_iam_role.start_step_functions.arn}"
handler = "main.lambda_handler"
runtime = "python2.7"
environment {
variables {
stepfunction_arn = "arn:aws:stepfunction:us-west-2:${data.aws_caller_identity.current.account_id}:stateMachine:AmiSecurityValidator"
}
}
}
/*
------------------------
CLOUDTRAIL EVENT TRIGGER
------------------------
*/
resource "aws_cloudwatch_event_rule" "ami_security_validator" {
name = "ami_registration_trigger"
description = "Trigger AWS Step function on any API calls which create a new AMI"
event_pattern = <<PATTERN
{
"detail-type": ["AWS API Call via CloudTrail"],
"detail":{
"eventSource":["ec2.amazonaws.com"],
"eventName":["CreateImage","RegisterImage","CopyImage"]
}
}
PATTERN
}
resource "aws_cloudwatch_event_target" "ami_security_validator" {
rule = "${aws_cloudwatch_event_rule.ami_security_validator.name}"
target_id = "TriggerAmiImageInspection"
arn = "${aws_lambda_function.start_step_function.arn}"
}
resource "aws_lambda_permission" "ami_security_validator" {
statement_id = "AllowExecutionFromCWEvents"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.start_step_function.arn}"
principal = "events.amazonaws.com"
source_arn = "${aws_cloudwatch_event_rule.ami_security_validator.arn}"
}
{
"StartAt": "ParseEvent",
"States": {
"ParseEvent": {
"Comment": "Pull out the image ID in the triggering event",
"Type": "Pass",
"InputPath": "$.detail.responseElements.imageId",
"ResultPath": "$.image_id",
"Next": "LaunchTestInstance"
},
"LaunchTestInstance": {
"Comment": "Launch a spot instance for a single instance in the build VPC",
"Type": "Task",
"Resource": "arn:aws:lambda:us-west-2:REDACTED:function:launch_instance:$LATEST",
"ResultPath": "$.spot_request",
"Next": "CheckInstanceReady"
},
"CheckInstanceReady": {
"Comment": "Wait for the instance to reach the READY state",
"Type": "Task",
"Resource": "arn:aws:lambda:us-west-2:REDACTED:function:check_instance_ready:$LATEST",
"InputPath": "$.spot_request",
"ResultPath": "$.instance",
"Next": "RenameInstanceIdtoResource",
"Retry" : [
{
"ErrorEquals": [ "States.TaskFailed" ],
"IntervalSeconds": 120,
"MaxAttempts": 4,
"BackoffRate": 1.5
}
]
},
"RenameInstanceIdtoResource": {
"Comment": "Renames the instance property to resource_id for tagging",
"Type": "Pass",
"InputPath": "$.instance.instanceId",
"ResultPath": "$.resource_id",
"Next": "GetTagsforInstance"
},
"GetTagsforInstance": {
"Comment": "Pull out the scan_batch_key as a tag to apply to the instance",
"Type": "Pass",
"InputPath": "$.spot_request.scan_batch_key",
"ResultPath": "$.tags.scan_batch_key",
"Next": "TagInstanceForScanning"
},
"TagInstanceForScanning": {
"Comment": "Tag the instance for scanning",
"Type": "Task",
"Resource": "arn:aws:lambda:us-west-2:REDACTED:function:tag_ec2_resource:$LATEST",
"ResultPath": null,
"Next": "StartInspectorAssessmentRun"
},
"StartInspectorAssessmentRun": {
"Comment": "Begin the assessment run",
"Type": "Task",
"Resource": "arn:aws:lambda:us-west-2:REDACTED:function:start_inpector_assessment_run:$LATEST",
"ResultPath": "$.assessment_run_arn",
"Next": "Wait15Minutes"
},
"Wait15Minutes": {
"Comment": "Pause for assessment execution",
"Type": "Wait",
"Seconds": 900,
"Next": "PollInspectorRunStatus"
},
"PollInspectorRunStatus": {
"Comment": "Check if the assessment is complete",
"Type": "Task",
"Resource": "arn:aws:lambda:us-west-2:REDACTED:function:check_inspector_assessment_run_complete:$LATEST",
"Next": "Parallel",
"Retry" : [
{
"ErrorEquals": [ "States.TaskFailed" ],
"IntervalSeconds": 120,
"MaxAttempts": 4,
"BackoffRate": 1.5
}
]
},
"Parallel": {
"Comment": "In parallel, terminate the assessment instance and parse the assessment findings",
"Type": "Parallel",
"End": true,
"Branches": [
{
"StartAt": "TerminateInstance",
"States": {
"TerminateInstance": {
"Comment": "Terminate the assessment instance",
"Type": "Task",
"Resource": "arn:aws:lambda:us-west-2:REDACTED:function:terminate_instance:$LATEST",
"End": true
}
}
},
{
"StartAt": "ParseInspectorAssessmentRunFindings",
"States": {
"ParseInspectorAssessmentRunFindings": {
"Comment": "Fetch the findings for the assessment and determines Pass/Fail",
"Type": "Task",
"Resource": "arn:aws:lambda:us-west-2:REDACTED:function:parse_inspector_assessment_run_findings:$LATEST",
"InputPath": "$.assessment_run_arn",
"ResultPath": "$.tags",
"Next": "RenameImageIDtoResource"
},
"RenameImageIDtoResource": {
"Comment": "Renames the imageId property to resource for tagging",
"Type": "Pass",
"InputPath": "$.image_id",
"ResultPath": "$.resource_id",
"Next": "RenameScanResulttoTags"
},
"RenameScanResulttoTags": {
"Comment": "Renames the scan_result field to tags for tagging",
"Type": "Pass",
"InputPath": "$.scan_result",
"ResultPath": "$.tags.scan_result",
"Next": "TagAMIWithResults"
},
"TagAMIWithResults": {
"Comment": "Tags the instance with scan results",
"Type": "Task",
"Resource": "arn:aws:lambda:us-west-2:REDACTED:function:tag_ec2_resource:$LATEST",
"ResultPath": null,
"End": true
}
}
}
]
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment