{ "AWSTemplateFormatVersion" : "2010-09-09", "Description" : "AWS Security Info - Detect EC2 instances not running SSM", "Parameters" : { "SlackWebhook" : { "Type" : "String", "Description" : "Provide a Slack Webhook if you want to get feedback through a Slack channel on the outcome of the scanner." }, "cron" : { "Type" : "String", "Default" : "cron(0 12 * * ? *)", "Description" : "Specify the schedule at which the event will trigger." }, "additional" : { "Type" : "String", "Default" : "", "Description" : "Friendly name for this AWS account" } }, "Resources" : { "myLambdaExecutionRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": ["lambda.amazonaws.com"]}, "Action": ["sts:AssumeRole"] }] }, "Path": "/", "Policies": [{ "PolicyName" : "LambdaPolicy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["logs:CreateLogGroup", "logs:CreateLogStream" , "logs:PutLogEvents"], "Resource": "arn:aws:logs:*:*:*" }, { "Effect": "Allow", "Action": ["ec2:ListInstances", "ec2:DescribeInstances", "ssm:DescribeInstanceInformation" ] , "Resource" : "*" } ] } }] } }, "myLambdaFunction": { "Type": "AWS::Lambda::Function", "Properties": { "Handler": "index.lambda_handler", "Role": { "Fn::GetAtt": [ "myLambdaExecutionRole", "Arn" ] }, "Description" : "AWS Security Info - Detect EC2 instances without SSM", "Code": { "ZipFile": { "Fn::Join": ["\n", [ "import boto3", "import urllib.request", "import os", "import json", "", "def lambda_handler(event, context):", " additional = os.environ['additional']", " region = os.environ['AWS_REGION']", " SlackWebhook = os.environ['SlackWebhook']", "", " ssm = []", " for p in boto3.client('ssm',region_name = region).get_paginator('describe_instance_information').paginate():", " for i in p['InstanceInformationList']:", " ssm.append(i['InstanceId'])", "", " message = ''", " for p in boto3.client('ec2',region_name = region).get_paginator('describe_instances').paginate(Filters=[", " {", " \"Name\": \"instance-state-name\",", " \"Values\": [\"running\"],", " }", " ]):", " for reservation in p['Reservations']:", " for instance in reservation['Instances']:", " PrivateIpAddress = instance['PrivateIpAddress']", " InstanceId = instance['InstanceId']", " if InstanceId not in ssm:", " message += f'- *{InstanceId}* ({PrivateIpAddress})\\n'", "", " if message != '':", " req = urllib.request.Request(SlackWebhook,", " json.dumps({'text': f'EC2 instances without SSM enabled on *{additional}*\\n' + message}).encode('utf-8'),", " {'Content-Type': 'application/json'}", " )", " resp = urllib.request.urlopen(req)", " response = resp.read()", "", " return {", " 'statusCode': 200,", " 'body': json.dumps('Hello from Lambda!')", " }" ]]} }, "Runtime": "python3.9", "FunctionName": { "Fn::Sub": "${AWS::StackName}-AWSSecurityTrigger" }, "Timeout": 25, "TracingConfig": { "Mode": "Active" }, "Environment" : { "Variables" : { "additional" : { "Ref" : "additional"}, "SlackWebhook" : { "Ref" : "SlackWebhook"} } } } }, "myEventSchedule" : { "Type": "AWS::Events::Rule", "Properties": { "Description": "Scheduled event to trigger the Lambda function", "ScheduleExpression" : { "Ref" : "cron" }, "State": "ENABLED", "Targets": [{ "Arn": { "Fn::GetAtt": [ "myLambdaFunction", "Arn" ] }, "Id" : { "Fn::Sub": "${AWS::StackName}-AWSSecurityTrigger" } } ] } }, "myEventPermissions" : { "Type": "AWS::Lambda::Permission", "Properties": { "FunctionName": { "Ref": "myLambdaFunction" }, "Action": "lambda:InvokeFunction", "Principal": "events.amazonaws.com", "SourceArn": { "Fn::GetAtt": ["myEventSchedule", "Arn"] } } } } }