├── README.md ├── 03 ├── guidance.txt ├── lambda_permissions.json └── lambda_function.py ├── 01 └── guidance.txt ├── 05 ├── role_trust_policy.json ├── guidance.txt ├── role_permission_template.json ├── delete_failed_rule.sh └── consolidate_events.sh ├── 04 ├── guidance.txt ├── lambda_permissions.json └── lambda_function.py ├── 02 └── guidance.txt ├── 07 └── guidance.txt └── 06 └── guidance.txt /README.md: -------------------------------------------------------------------------------- 1 | # aws-event-driven 2 | Github repo for the AWS Event-Driven Automation and Operations course by Chad Smith 3 | -------------------------------------------------------------------------------- /03/guidance.txt: -------------------------------------------------------------------------------- 1 | Create an IAM policy with the attached json document 2 | Create a lambda function with the attached code. 3 | 4 | Create an EventBridge rule that is scheduled daily with the new Lambda function as a target 5 | -------------------------------------------------------------------------------- /01/guidance.txt: -------------------------------------------------------------------------------- 1 | Navigate to the CloudWatch Log group that contains CloudTrail logs 2 | Click on Actions, then Create Metric Filter 3 | Enter the following text: 4 | { ($.eventName = "ChangePassword") } 5 | Click Next and enter the filter name and metric details 6 | 7 | -------------------------------------------------------------------------------- /05/role_trust_policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Principal": { 7 | "Service": "events.amazonaws.com" 8 | }, 9 | "Action": "sts:AssumeRole" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /05/guidance.txt: -------------------------------------------------------------------------------- 1 | In the AWS Console, 2 | Create an EventBridge rule in a single region to forward all events to the designated consolidation region 3 | 4 | That is a time-consuming activity, and much easier handled via automation 5 | Use the attached bash shell script with a list of regions to create rules in all other regions 6 | 7 | -------------------------------------------------------------------------------- /04/guidance.txt: -------------------------------------------------------------------------------- 1 | Create an SSM Parameter named the ID of the VPC in question 2 | The data value is the Security Group ID to add to each new instance 3 | 4 | Create a Lambda function with the attached Python code 5 | 6 | Create an EventBridge rule to capture all EC2 state changes for "pending" 7 | Use the newly created Lambda function as the target 8 | -------------------------------------------------------------------------------- /05/role_permission_template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Effect": "Allow", 6 | "Action": [ 7 | "events:PutEvents" 8 | ], 9 | "Resource": [ 10 | "EVENTBUSARN" 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /03/lambda_permissions.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "VisualEditor0", 6 | "Effect": "Allow", 7 | "Action": [ 8 | "ec2:DeleteVolume", 9 | "ec2:DescribeVolumes" 10 | ], 11 | "Resource": "*" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /03/lambda_function.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | 3 | def lambda_handler(event, context): 4 | 5 | regionid = event['region'] 6 | ec2 = boto3.resource("ec2", region_name=regionid) 7 | available_volumes = ec2.volumes.filter( 8 | Filters=[{'Name': 'status', 'Values': ['available']}] 9 | ) 10 | 11 | for volume in available_volumes: 12 | print volume.id 13 | volume.delete() 14 | return "Completed" 15 | -------------------------------------------------------------------------------- /04/lambda_permissions.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "VisualEditor0", 6 | "Effect": "Allow", 7 | "Action": [ 8 | "ec2:Describe*", 9 | "ssm:DescribeParameters", 10 | "ec2:ModifyInstanceAttribute*", 11 | "ssm:GetParameters", 12 | "ssm:GetParameter" 13 | ], 14 | "Resource": "*" 15 | } 16 | ] 17 | } 18 | 19 | -------------------------------------------------------------------------------- /02/guidance.txt: -------------------------------------------------------------------------------- 1 | Navigate to the CloudWatch dashboard, in Metrics->All Metrics 2 | Search for StatusCheckFailed_System metrics 3 | Verify that at least one exists, then check to All Alarms, then Create Alarm 4 | Paste in StatusCheckFailed_System and select the desired instance 5 | Change the threshold to >= 1 6 | Under Additional Configuration, you may want to increase the time the instance must be unavailable 7 | Change "1 out of 1" to 4 out of 4 or higher, where each increment is 5 extra minutes 8 | Click Next 9 | 10 | Scroll down to EC2 action and select the "Recover this instance" radio button 11 | Note that some instance types do not qualify for Instance Recovery 12 | Optionally you can choose to either configure notification or remove it entirely 13 | 14 | Click on "Create alarm" 15 | -------------------------------------------------------------------------------- /07/guidance.txt: -------------------------------------------------------------------------------- 1 | Log into the AWS Organizations Management Account 2 | Navigate to the WAF and Shield dashboard 3 | Select AWS Firewall Manager 4 | In the Enable Wizard, paste in the 12-digit account id for the Firewall Manager account 5 | Wait for a couple minutes for enabling to complete 6 | 7 | Log into the AWS Firewall Manager account 8 | Navigate to the WAF and Shield dashboard 9 | Select AWS Firewall Manager 10 | Click on Security Policies and select Create Policy 11 | Select Security Group 12 | Select Auditing and enforcement of security group rules 13 | Select a Region 14 | Enter a policy name 15 | Select onfigure managed audit policy rules 16 | Select Inbound Rules 17 | Select Audit overly permissive security group rules 18 | Select Deny rules with the allow 'ALL' protocol 19 | Select Auto remediate any noncompliant resources. 20 | Click on Next 21 | Select Include all accounts under my AWS organization 22 | Select Security group 23 | Select Include all resources that match the selected resource type 24 | Click on Next 25 | Click on Next 26 | Click on Create Policy 27 | -------------------------------------------------------------------------------- /05/delete_failed_rule.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Consolidates EventBridge events into a single region 3 | CENTRALREGION=$1 4 | ACCTID=$2 5 | PROFILE=$3 6 | EVENTBUSARN="arn:aws:events:${CENTRALREGION}:${ACCTID}:event-bus/default" 7 | 8 | #Create an IAM role required for cross-region event target 9 | #ROLEARN=`aws iam create-role --role-name EventBusConsolidationRole --assume-role-policy-document file://role_trust_policy.json --query Role.Arn` 10 | 11 | #Substitute the variable in the template permission policy with the correct event bus ARN 12 | #sed "s|EVENTBUSARN|${EVENTBUSARN}|g" role_permission_template.json > role_permission_policy.json 13 | 14 | #Create an IAM policy from the updated permission policy 15 | #IAMPOLICYARN=`aws iam create-policy --policy-name EventBusConsolidationPolicy --policy-document file://role_permission_policy.json --query Policy.Arn` 16 | 17 | #Attach the new IAM policy to the role 18 | #aws iam attach-role-policy --role-name EventBusConsolidationRole --policy-arn $IAMPOLICYARN 19 | 20 | 21 | #Create the EventBridge rule which forwards all events to the central region using the newly created IAM role 22 | 23 | REGIONS=`aws ec2 describe-regions --output text --query Regions[].RegionName |tr -s '\t' '\n'` 24 | for i in $REGIONS; do 25 | if [[ $i != $CENTRALREGION ]]; then 26 | echo "Deleting rule in region $i" 27 | aws --profile $PROFILE events --region $i remove-targets --rule "EventConsolidation" --id 1 28 | aws --profile $PROFILE events --region $i delete-rule --name "EventConsolidation" 29 | fi 30 | done 31 | -------------------------------------------------------------------------------- /06/guidance.txt: -------------------------------------------------------------------------------- 1 | SNS Service 2 | 3 | Configure an SNS topic to receive alerts (no subscribers needed) 4 | 5 | ------------------------------------------------------------------------------------------------------ 6 | EventBridge Service 7 | 8 | Configure an EventBridge rule which captures high severity GuardDuty findings with the following JSON: 9 | (do this in your primary region if you have all other regions forwarding events) 10 | 11 | { 12 | "source": ["aws.guardduty"], 13 | "detail-type": ["GuardDuty Finding"], 14 | "detail": { 15 | "severity": [7.0,7.1,7.2,7.3,7.4,7.5,7.6,7.7,7.8,7.9,8.0,8.1,8.2,8.3,8.4,8.5,8.6,8.7,8.8,8.9,7,8] 16 | } 17 | } 18 | 19 | For the rule, select your SNS topic as the target 20 | 21 | ------------------------------------------------------------------------------------------------------ 22 | Chatbot service 23 | 24 | Log in to a web browser using your Slack identity (this will be used by Chatbot) 25 | Navigate to the Chatbot console in the same region as your event consolidation 26 | Click on Configure New Client and select Slack 27 | Select Allow for permissions 28 | 29 | On the following page, click on Configure New Channel 30 | Enter a descriptive name for the channel 31 | Select the channel type (mine are Public, so I used that) 32 | Select the channel name from the drop down list 33 | Leave the Channel IAM role radio button selected 34 | Make sure that Create an IAM role using a template is selected and enter a new role name 35 | Leave Policy Templates with the default value 36 | In Channel guardrails, Select AWSChatbotServiceLinkedRolePolicy 37 | In the bottom drop down menus, select the SNS topic region and the topic itself 38 | click Configure 39 | -------------------------------------------------------------------------------- /05/consolidate_events.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Consolidates EventBridge events into a single region 3 | CENTRALREGION=$1 4 | ACCTID=$2 5 | PROFILE=$3 6 | EVENTBUSARN="arn:aws:events:${CENTRALREGION}:${ACCTID}:event-bus/default" 7 | 8 | #Create an IAM role required for cross-region event target 9 | ROLEARN=`aws --region us-east-1 --output text --profile $PROFILE iam create-role --role-name EventBusConsolidationRole --assume-role-policy-document file://role_trust_policy.json --query Role.Arn` 10 | echo "role arn is $ROLEARN" 11 | 12 | #Substitute the variable in the template permission policy with the correct event bus ARN 13 | sed "s|EVENTBUSARN|${EVENTBUSARN}|g" role_permission_template.json > role_permission_policy.json 14 | 15 | #Create an IAM policy from the updated permission policy 16 | IAMPOLICYARN=`aws --region us-east-1 --output text --profile $PROFILE iam create-policy --policy-name EventBusConsolidationPolicy --policy-document file://role_permission_policy.json --query Policy.Arn` 17 | echo "iam policy ARN is $IAMPOLICYARN" 18 | #Attach the new IAM policy to the role 19 | aws --region us-east-1 --output text --profile $PROFILE iam attach-role-policy --role-name EventBusConsolidationRole --policy-arn $IAMPOLICYARN 20 | 21 | 22 | #Create the EventBridge rule which forwards all events to the central region using the newly created IAM role 23 | 24 | REGIONS=`aws --region us-east-1 --output text --profile $PROFILE ec2 describe-regions --query Regions[].RegionName |tr -s '\t' '\n'` 25 | for i in $REGIONS; do 26 | if [[ $i != $CENTRALREGION ]]; then 27 | echo "Creating rule in region $i" 28 | aws --profile $PROFILE events --region $i put-rule --name "EventConsolidation" --event-pattern "{\"account\":[\"${ACCTID}\"]}" 29 | aws --profile $PROFILE events --region $i put-targets --rule "EventConsolidation" --targets "Id"="1","Arn"="${EVENTBUSARN}","RoleArn"="${ROLEARN}" 30 | fi 31 | done 32 | -------------------------------------------------------------------------------- /04/lambda_function.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | import logging 4 | logger = logging.getLogger() 5 | logger.setLevel(logging.INFO) 6 | 7 | def lambda_handler(event, context): 8 | logger.info(event) 9 | 10 | #fun translation of event into something Python can use 11 | event_dict=json.dumps(event) 12 | event_json=json.loads(event_dict) 13 | detail=event_json['detail'] 14 | logger.info("Detail =%s", detail) 15 | 16 | #extract Instance ID from the event 17 | instance_id=detail['instance-id'] 18 | 19 | logger.info("Instance ID =%s", instance_id) 20 | 21 | #get the ec2 object 22 | client = boto3.client('ec2') 23 | newinstanceraw = client.describe_instances( 24 | Filters=[ 25 | {'Name': 'instance-id', 'Values': [instance_id]} 26 | ] 27 | ) 28 | #fun translation of instance object data into something Python can use 29 | newinstancedict = json.dumps(newinstanceraw, default=str) 30 | newinstance = json.loads(newinstancedict) 31 | 32 | #extract secgroup ID list for the instance 33 | sgids = [item.get('GroupId') for item in newinstance['Reservations'][0]['Instances'][0]['NetworkInterfaces'][0]['Groups']] 34 | logger.info("SGIDs=%s", sgids) 35 | 36 | #extract the VPC ID for the instance 37 | vpcid = newinstance['Reservations'][0]['Instances'][0]['VpcId'] 38 | logger.info("VPCID=%s", vpcid) 39 | 40 | #extract the VPC-specific secgroup ID for VPN access 41 | ssm = boto3.client('ssm') 42 | sgidparameter = ssm.get_parameter(Name=vpcid) 43 | vpnsgid = sgidparameter['Parameter']['Value'] 44 | logger.info("VPN secgroup id = %s", vpnsgid) 45 | 46 | #append the vpn sgid to the list of secgroups 47 | sgids.append(vpnsgid) 48 | logger.info("SGIDs=%s", sgids) 49 | 50 | #update the instance attributes to include new secgroup 51 | #this is an idempotent action, so if the secgroup already attached, no error 52 | secgroupadd = client.modify_instance_attribute( 53 | InstanceId=instance_id, 54 | Groups=sgids 55 | ) 56 | logger.info("output of secgroupadd=%s", secgroupadd) 57 | 58 | --------------------------------------------------------------------------------