Last active
January 5, 2024 12:20
-
-
Save davidski/788afdb2a79d77edfb5fea65b50369e7 to your computer and use it in GitHub Desktop.
Step Function Definition for AMI Security Validator
This file contains hidden or 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
# 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}" | |
} |
This file contains hidden or 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
{ | |
"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