Yeobot Cloudformation Template

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Permission to run CloudNative's Yeobot in this AWS account",

The version of CloudFormation templates that we are using and a description of this template.

    "Mappings": {
        "RegionMap": {
            "ap-southeast-1": {
                "ServiceToken": "arn:aws:sns:ap-southeast-1:860421987956:yeobot-cfn-callback"
            },
            "eu-west-1": {
                "ServiceToken": "arn:aws:sns:eu-west-1:860421987956:yeobot-cfn-callback"
            },
            "us-east-1": {
                "ServiceToken": "arn:aws:sns:us-east-1:860421987956:yeobot-cfn-callback"
            },
            "us-west-2": {
                "ServiceToken": "arn:aws:sns:us-west-2:860421987956:yeobot-cfn-callback"
            }
        }
    },

These are the Amazon Resource Names (ARNs) of our SNS Topics in each region supported by AWS Lambda. These will be notified when you create, update or delete your CloudFormation Stack.

    "Outputs": {
        "YeobotPolicyVersionID": {
            "Description": "The version ID for these roles and policies",
            "Value": "20160211"
        },
        "YeobotRoleARN": {
            "Description": "The ARN of the IAM Role needed by Yeobot",
            "Value": {
                "Fn::GetAtt": [
                    "YeobotRole",
                    "Arn"
                ]
            }
        }
    },

This information is available on the "Output" tab of your CloudFormation Stack. It gives us variables we need to connect your account with ours.

    "Resources": {
        "Callback": {
            "Properties": {
                "AwsAccountId": {
                    "Ref": "AWS::AccountId"
                },
                "AwsRegion": {
                    "Ref": "AWS::Region"
                },
                "CloudNativeId": "88888888-4444-4444-4444-121212121212",
                "RelayFunction": {
                    "Fn::GetAtt": [
                        "YeobotEventRelayFn",
                        "Arn"
                    ]
                },
                "RoleArn": {
                    "Fn::GetAtt": [
                        "YeobotRole",
                        "Arn"
                    ]
                },
                "RoleExternalId": "88888888-4444-4444-4444-121212121212",
                "ServiceToken": {
                    "Fn::FindInMap": [
                        "RegionMap",
                        {
                            "Ref": "AWS::Region"
                        },
                        "ServiceToken"
                    ]
                },
                "StackId": {
                    "Ref": "AWS::StackId"
                },
                "StackName": {
                    "Ref": "AWS::StackName"
                },
                "TemplateVersion": "20160211"
            },
            "Type": "Custom::Callback"
        },

This section defines a custom resource that sends key/value pairs to our SNS Topic. The key elements here are CloudNativeId, which is your ID in our systems, and RoleExternalId, which is an ID we've assigned that is unique to your AWS Account. In your own CF file, these IDs will be different.

        "LimitReadOnlyAccessPolicy": {
            "Properties": {
                "Description": "This policy limits the amount of read-only access CloudNative has. The AWS supplied read-only policy is too permissive so this denies those permissions.",
                "PolicyDocument": {
                    "Statement": [

This next section is the actual IAM policy that we are defining. We start with the default read only policy from Amazon (defined in YeobotRole below), and then remove access to your data, so we only have access to what we absolutely need. We then add in some additional access. See below for details.

                        {
                            "Action": [
                                "cloudformation:GetTemplate",
                                "dynamodb:GetItem",
                                "dynamodb:BatchGetItem",
                                "dynamodb:Query",
                                "dynamodb:Scan",
                                "ec2:GetConsoleOutput",
                                "kinesis:Get*",
                                "lambda:GetFunction",
                                "s3:GetObject",
                                "sdb:Select*",
                                "sqs:ReceiveMessage"
                            ],
                            "Effect": "Deny",
                            "Resource": [
                                "*"
                            ],
                            "Sid": "ReadOnlyAccessDenyData"
                        },

This is the list of permissions that we are removing from the default read only policy that we don't need. This prevents CloudNative from accessing any of your data.

                        {
                            "Action": [
                                "events:EnableRule",
                                "events:PutEvents",
                                "events:PutRule",
                                "events:PutTargets"
                            ],
                            "Effect": "Allow",
                            "Resource": [
                                "*"
                            ],
                            "Sid": "AddCloudWatchEvents"
                        },
                    ],
                    "Version": "2012-10-17"
                },
                "Roles": [
                    {
                        "Ref": "YeobotRole"
                    }
                ]
            },
                "Type": "AWS::IAM::ManagedPolicy"
            },

In this section we are adding in the ability to create CloudWatch Events rules and publish events. This helps us keep our model of your AWS account up-to-date in near real-time.

        "YeobotEventRelayFn": {
            "Properties": {
                "Code": {
                    "S3Bucket": "yeobot",
                    "S3Key": "yeobot_eventfn.zip"
                },
                "Handler": "yeobot_eventfn.handler",
                "Role": {
                    "Fn::GetAtt": [
                        "YeobotEventRelayFnRole",
                        "Arn"
                    ]
                },
                "Runtime": "python2.7"
            },
            "Type": "AWS::Lambda::Function"
        },

This is where we define our Lambda function that will run in your account, and where to download it. We have to run a Lambda function because currently Amazon does not allow CloudWatch Events to directly call a Lambda function in another account, so we have to call one in your account that then calls the function in our account (which is allowed). If you'd like to inspect the Lambda code we install in your account, you can download it and inspect it, or just read it here:

import boto3
import json

function_arn = 'arn:aws:lambda:us-west-2:433502988969:function:yeobot-relay:PROD'
lambda_client = boto3.client('lambda')


def handler(event, context):
    lambda_client.invoke(
        FunctionName=function_arn,
        InvocationType='Event',
        Payload=json.dumps(event))
    return {'status': 'success'}