{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "Ops Automator, template version v2.2.0, (SO0029)", "Mappings": { "Send": { "AnonymousUsage": { "Data": "Yes" } }, "Settings": { "Names": { "CloudTrailRdsEventRule": "OpsAutomatorCloudTrailRdsEventRule", "CloudWatchQueueHandlerLambdaFunction": "OpsAutomatorCloudWatchQueueHandler", "ConfigurationBucket": "", "EbsSnapshotEventRule": "OpsAutomatorEbsSnapshotEventRule", "Ec2StateEventRule": "OpsAutomatorEc2StateEventRule", "EventsTopic": "EventsTopic", "EventsTopicDisplayName": "OpsAutomatorEvents", "IssuesTopic": "IssuesTopic", "IssuesTopicDisplayName": "Issues", "LogQueueName": "OpsAutomatorCloudWatchLogQueue", "NotificationsTopic": "NotificationsTopic", "NotificationsTopicDisplayName": "Notifications", "OpsAutomatorCompletionRule": "OpsAutomatorCompletionRule", "OpsAutomatorLambdaFunction": "OpsAutomator", "OpsAutomatorRule": "OpsAutomatorRule", "ReportingBucket": "", "ResourcesBucket": "", "TaggingEventRule": "OpsAutomatorTaggingEventRule" }, "Metrics": { "Url": "https://metrics.awssolutionsbuilder.com/generic", "SolutionId": "S00029" }, "Templates": { "OptimizeCrossAccountTemplate": "True" }, "ActionMemory": { "Standard": "128", "Medium": "256", "Large": "512", "XLarge": "1024", "XXLarge": "2048", "XXXLarge": "3008", "ECS": "" }, "Resources": { "ResourceToS3SizeKB": 16, "EncryptResourceData": "True" }, "ServiceLimits": { "MaxConcurrentEbsSnapshotCopies": 5, "MaxConcurrentRdsSnapshotCopies": 5, "MaxConcurrentImageCopies": 25 }, "CloudWatchApiLimits": { "MaxPutCallsPerStream": 5, "MaxDescribeCalls": 5, "MaxApiCalls": 40 }, "VpcCidrs": { "vpc": "10.0.0.0/16", "pubsubnet1": "10.0.0.0/24", "pubsubnet2": "10.0.1.0/24" } }, "EnabledDisabled": { "Yes": { "Value": "ENABLED" }, "No": { "Value": "DISABLED" } }, "YesNoBoolean": { "Yes": { "Value": "True" }, "No": { "Value": "False" } } }, "Parameters": { "TagName": { "Type": "String", "Default": "OpsAutomatorTaskList", "MinLength": 1, "MaxLength": 127, "Description": "Default name of tag that contains the list of tasks for a tagged resources." }, "EnableTaskCleanup": { "Type": "String", "AllowedValues": [ "Yes", "No" ], "Default": "Yes", "Description": "Enable cleanup of task tracking table." }, "EnableTaskExport": { "Type": "String", "AllowedValues": [ "Yes", "No" ], "Default": "No", "Description": "Enable S3 export of task tracking table." }, "RetainFailedTasks": { "Type": "String", "AllowedValues": [ "Yes", "No" ], "Default": "Yes", "Description": "Do not delete failed tasks." }, "UseCloudWatchMetrics": { "Type": "String", "AllowedValues": [ "Yes", "No" ], "Default": "Yes", "Description": "Collect CloudWatch metrics data for Ops Automator. Detailed metrics for individual tasks can be configured at task level." }, "TaskRetentionHours": { "Type": "Number", "MinValue": 1, "Default": 168, "Description": "Number of hours after which tasks are deleted." }, "LogRetentionDays": { "Type": "Number", "Default": 30, "AllowedValues": [ 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653 ], "Description": "Retention days for Ops Automator logs." }, "ConfigBackupDays": { "Type": "Number", "Default": 30, "MinValue": 1, "Description": "Retention days for Ops Automator configuration backups." }, "SchedulerActive": { "Type": "String", "AllowedValues": [ "Yes", "No" ], "Default": "Yes", "Description": "Activate or deactivate scheduling of task." }, "VpcId": { "Type": "String", "Description": "Optional - VPC Id of existing VPC. Leave blank to have a new VPC created", "Default": "", "AllowedPattern": "^(?:vpc-[0-9a-f]{8,17}|)$", "ConstraintDescription": "VPC Id must begin with 'vpc-' or leave blank to have a new VPC created" }, "SubnetIds": { "Type": "CommaDelimitedList", "Description": "Optional - Comma separated list of two (2) existing VPC Subnet Ids where ECS instances will run. Required if setting VPC.", "Default": "" }, "VpcAvailabilityZones": { "Type": "CommaDelimitedList", "Description": "Optional - Comma-delimited list of VPC availability zones in which to create subnets. Required if setting VPC.", "Default": "" }, "ECSFargate": { "Type": "String", "Description": "Should Ops Automator use Fargate to run tasks?", "AllowedValues": [ "Yes", "No" ], "Default": "No" } }, "Metadata": { "AWS::CloudFormation::Interface": { "ParameterGroups": [ { "Label": { "default": "Ops Automator (version v2.2.0)" }, "Parameters": [ "TagName", "UseCloudWatchMetrics", "SchedulerActive" ] }, { "Label": { "default": "Maintenance" }, "Parameters": [ "EnableTaskCleanup", "EnableTaskExport", "TaskRetentionHours", "RetainFailedTasks", "LogRetentionDays", "ConfigBackupDays" ] }, { "Label": { "default": "ECS/Fargate Parameters" }, "Parameters": [ "ECSFargate", "VpcId", "SubnetIds", "VpcAvailabilityZones" ] } ], "ParameterLabels": { "EnableTaskCleanup": { "default": "Cleanup Task Tracking Table" }, "EnableTaskExport": { "default": "Export Task Tracking Table to Amazon S3" }, "RetainFailedTasks": { "default": "Keep failed tasks" }, "SchedulerActive": { "default": "Scheduling active" }, "TagName": { "default": "TaskScheduler Tagname" }, "TaskRetentionHours": { "default": "Hours to keep tasks" }, "ConfigBackupDays": { "default": "Days to keep configuration backups" }, "UseCloudWatchMetrics": { "default": "Enable CloudWatch Metrics" }, "VpcId": { "default": "Cluster VPC" }, "SubnetIds": { "default": "Cluster Subnets" }, "VpcAvailabilityZones": { "default": "Cluster Availability zones" }, "ECSFargate": { "default": "ECS/Fargate" } } } }, "Conditions": { "CustomConfigBucketName": { "Fn::Not": [ { "Fn::Equals": [ { "Fn::FindInMap": [ "Settings", "Names", "ConfigurationBucket" ] }, "" ] } ] }, "CustomReportingBucketName": { "Fn::Not": [ { "Fn::Equals": [ { "Fn::FindInMap": [ "Settings", "Names", "ReportingBucket" ] }, "" ] } ] }, "CustomResourcesBucketName": { "Fn::Not": [ { "Fn::Equals": [ { "Fn::FindInMap": [ "Settings", "Names", "ResourcesBucket" ] }, "" ] } ] }, "EnableTaskCleanupCondition": { "Fn::Equals": [ { "Ref": "EnableTaskCleanup" }, "Yes" ] }, "EnableTaskExportCondition": { "Fn::Equals": [ { "Ref": "EnableTaskExport" }, "Yes" ] }, "KeepFailedTasksCondition": { "Fn::Equals": [ { "Ref": "RetainFailedTasks" }, "Yes" ] }, "EncryptResourceData": { "Fn::Equals": [ { "Fn::FindInMap": [ "Settings", "Resources", "EncryptResourceData" ] }, "True" ] }, "CloudWatchPutLimit800": { "Fn::Or": [ { "Fn::Equals": [ { "Ref": "AWS::Region" }, "ap-south-1" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "ap-northeast-1" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "ap-northeast-2" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "ap-southeast-1" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "ap-southeast-2" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "eu-central-1" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "eu-west-2" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "sa-east-1" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "us-east-2" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "us-west-1" ] } ] }, "FifoQueue": { "Fn::Or": [ { "Fn::Equals": [ { "Ref": "AWS::Region" }, "eu-west-1" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "ap-northeast-1" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "ap-southeast-2" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "us-east-1" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "us-east-2" ] }, { "Fn::Equals": [ { "Ref": "AWS::Region" }, "us-west-2" ] } ] }, "UseEcs": { "Fn::Not": [ { "Fn::Equals": [ { "Ref": "ECSFargate" }, "No" ] } ] }, "CreateVpcResources": { "Fn::And": [ { "Fn::Equals": [ { "Ref": "VpcId" }, "" ] }, { "Condition": "UseEcs" } ] }, "UseSpecifiedVpcAvailabilityZones": { "Fn::Not": [ { "Fn::Equals": [ { "Fn::Join": [ "", { "Ref": "VpcAvailabilityZones" } ] }, "" ] } ] } }, "Resources": { "OpsAutomatorEventsForwardRole": { "Type": "AWS::IAM::Role", "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W11", "reason": "logs:*LogStream actions requires * resource." } ] } }, "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }, "Policies": [ { "PolicyName": "OpsAutomatorEventForwardRolePolicy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "sns:Publish" ], "Resource": { "Ref": "OpsAutomatorEventsTopic" } }, { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*" }, { "Sid": "KMS", "Effect": "Allow", "Resource": { "Fn::GetAtt": [ "ResourceEncryptionKey", "Arn" ] }, "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:DescribeKey" ] } ] } } ], "Path": "/" } }, "OpsAutomatorCloudWatchLogHandlerRole": { "Type": "AWS::IAM::Role", "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W11", "reason": "logs:DescribeLogStreams actions requires * resource." } ] } }, "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }, "Policies": [ { "PolicyName": "OpsAutomatorCloudWatchHandlerRolePolicy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:GetRecords", "dynamodb:GetShardIterator", "dynamodb:DescribeStream", "dynamodb:ListStreams" ], "Resource": [ { "Fn::GetAtt": [ "TriggerTable", "StreamArn" ] } ] }, { "Effect": "Allow", "Action": [ "dynamodb:PutItem" ], "Resource": [ { "Fn::Join": [ "", [ { "Fn::Join": [ ":", [ "arn:aws:dynamodb", { "Ref": "AWS::Region" }, { "Ref": "AWS::AccountId" }, "table/" ] ] }, { "Ref": "TriggerTable" } ] ] } ] }, { "Effect": "Allow", "Action": [ "logs:DescribeLogStreams" ], "Resource": [ "*" ] }, { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*" }, { "Sid": "KMS", "Effect": "Allow", "Resource": { "Fn::GetAtt": [ "ResourceEncryptionKey", "Arn" ] }, "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:DescribeKey" ] } ] } } ], "Path": "/" } }, "OpsAutomatorLambdaRole": { "Type": "AWS::IAM::Role", "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W11", "reason": "The only wildcard (*) resource on this role is grouped into a Policy Statement with SID: ActionsRequireAllResources. All actions in the ActionsRequireAllResources Policy Statement support all resources." }, { "id": "F38", "reason": "To allow the IAM:PassRole action. A condition to allow only lambda to assume the role has also been added." }, { "id": "W76", "reason": "Complex role needs refactoring. This will be addressed in a future release." } ] } }, "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" }, { "Effect": "Allow", "Principal": { "Service": "dynamodb.amazonaws.com" }, "Action": "sts:AssumeRole" }, { "Effect": "Allow", "Principal": { "Service": "events.amazonaws.com" }, "Action": "sts:AssumeRole" }, { "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": [ "sts:AssumeRole" ] } ] }, "Policies": [ { "PolicyName": "OpsAutomatorLambdaRolePolicy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "logs:PutRetentionPolicy", "logs:DescribeLogStreams" ], "Resource": [ { "Fn::Join": [ ":", [ "arn:aws:logs", { "Ref": "AWS::Region" }, { "Ref": "AWS::AccountId" }, "log-group", { "Ref": "OpsAutomatorLogGroup" }, "*" ] ] }, { "Fn::Join": [ ":", [ "arn:aws:logs", { "Ref": "AWS::Region" }, { "Ref": "AWS::AccountId" }, "log-group:/aws/lambda/*" ] ] } ] }, { "Effect": "Allow", "Action": [ "sns:Publish" ], "Resource": [ { "Ref": "OpsAutomatorIssuesTopic" }, { "Ref": "OpsAutomatorNotificationsTopic" } ] }, { "Effect": "Allow", "Action": [ "sns:Publish", "sns:RemovePermission", "sns:AddPermission", "sns:GetTopicAttributes", "sns:Subscribe" ], "Resource": [ { "Ref": "OpsAutomatorEventsTopic" } ] }, { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:DeleteObject", "s3:GetObject" ], "Resource": [ { "Fn::Sub": [ "arn:aws:s3:::${Name}/*", { "Name": { "Ref": "TaskResources" } } ] } ] }, { "Effect": "Allow", "Action": "s3:PutObject", "Resource": [ { "Fn::Sub": [ "arn:aws:s3:::${Name}/*", { "Name": { "Ref": "Reporting" } } ] } ] }, { "Effect": "Allow", "Action": [ "dynamodb:UpdateItem", "dynamodb:GetItem", "dynamodb:BatchWriteItem", "dynamodb:Query" ], "Resource": [ { "Fn::GetAtt": [ "TaskTrackingTable", "Arn" ] }, { "Fn::Join": [ "", [ { "Fn::GetAtt": [ "TaskTrackingTable", "Arn" ] }, "/index/*" ] ] } ] }, { "Effect": "Allow", "Action": [ "dynamodb:Query", "dynamodb:Scan", "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem" ], "Resource": { "Fn::GetAtt": [ "ConfigurationTable", "Arn" ] } }, { "Sid": "ConcurrencyTableAccess", "Effect": "Allow", "Action": [ "dynamodb:UpdateItem", "dynamodb:GetItem", "dynamodb:DeleteItem" ], "Resource": { "Fn::GetAtt": [ "ConcurrencyTable", "Arn" ] } }, { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:UpdateItem" ], "Resource": { "Fn::GetAtt": [ "LastSchedulerExecutionTable", "Arn" ] } }, { "Effect": "Allow", "Action": "dynamodb:PutItem", "Resource": { "Fn::GetAtt": [ "TriggerTable", "Arn" ] } }, { "Effect": "Allow", "Action": [ "events:EnableRule", "events:DisableRule" ], "Resource": [ { "Fn::Sub": [ "arn:aws:events:${AWS::Region}:${AWS::AccountId}:rule/${AWS::StackName}-${Rule}", { "Rule": { "Fn::FindInMap": [ "Settings", "Names", "OpsAutomatorRule" ] } } ] }, { "Fn::Sub": [ "arn:aws:events:${AWS::Region}:${AWS::AccountId}:rule/${AWS::StackName}-${Rule}", { "Rule": { "Fn::FindInMap": [ "Settings", "Names", "OpsAutomatorCompletionRule" ] } } ] } ] }, { "Effect": "Allow", "Action": [ "cloudformation:GetTemplate", "cloudformation:ListStackResources" ], "Resource": [ { "Fn::Sub": "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*" } ] }, { "Effect": "Allow", "Action": [ "lambda:InvokeFunction", "lambda:GetFunction" ], "Resource": [ { "Fn::Join": [ ":", [ "arn:aws:lambda", { "Ref": "AWS::Region" }, { "Ref": "AWS::AccountId" }, "function", { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "OpsAutomatorLambdaFunction" ] }, "Standard" ] ] } ] ] } ], "Sid": "OpsAutomatorLambdaInvoke" }, { "Effect": "Allow", "Action": "ecs:RunTask", "Resource": { "Fn::Sub": "arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:task-definition/${AWS::StackName}-ops-automator-task:*" } }, { "Effect": "Allow", "Action": [ "ec2:RunInstances", "ec2:TerminateInstance" ], "Resource": { "Fn::Join": [ ":", [ "arn:aws:ec2:*", { "Ref": "AWS::AccountId" }, "instance/*" ] ] } }, { "Sid": "ActionsRequireAllResources", "Effect": "Allow", "Action": [ "cloudformation:DeleteStack", "cloudwatch:PutMetricData", "dynamodb:ListTables", "ec2:DescribeVpcs", "ec2:DescribeSubnets", "ec2:DescribeImages", "ec2:DescribeInternetGateways", "ec2:DescribeSecurityGroups", "ec2:DescribeSubnets", "ec2:DescribeVpcEndpoints", "ec2:DescribeRouteTables", "ec2:DescribeNatGateways", "iam:PassRole", "events:PutRule", "events:ListRules", "logs:DescribeLogGroups", "pricing:GetAttributeValues", "ssm:GetParameter", "ssm:GetParameters", "sts:AssumeRole", "tag:GetResources", "s3:ListBuckets", "dynamodb:ListStreams" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "dynamodb:DescribeStream", "dynamodb:GetRecords", "dynamodb:GetShardIterator", "dynamodb:ListStreams" ], "Resource": [ { "Fn::GetAtt": ["ConfigurationTable", "StreamArn"] }, { "Fn::GetAtt": ["TaskTrackingTable", "StreamArn"] }, { "Fn::GetAtt": ["ConcurrencyTable", "StreamArn"] }, { "Fn::GetAtt": ["TriggerTable", "StreamArn"] } ] }, { "Sid": "KMS", "Effect": "Allow", "Resource": { "Fn::GetAtt": [ "ResourceEncryptionKey", "Arn" ] }, "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:DescribeKey" ] } ] } } ], "Path": "/" } }, "EcsExecutionRole": { "Type": "AWS::IAM::Role", "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W11", "reason": "GetAuthorizationToken requires global resource" } ] } }, "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "Service": "ecs-tasks.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }, "Policies": [ { "PolicyName": "OpsAutomatorLambdaRolePolicy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "ecr:GetAuthorizationToken", "Resource": "*" }, { "Effect": "Allow", "Action": [ "ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage" ], "Resource": { "Fn::Sub": "arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/ops-automator" } }, { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": [ { "Fn::Sub": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${AWS::StackName}-logs" }, { "Fn::Sub": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${AWS::StackName}-logs:log-stream:*" } ] }, { "Sid": "KMS", "Effect": "Allow", "Resource": { "Fn::GetAtt": [ "ResourceEncryptionKey", "Arn" ] }, "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:DescribeKey" ] } ] } } ] } }, "LastSchedulerExecutionTable": { "Type": "AWS::DynamoDB::Table", "Properties": { "AttributeDefinitions": [ { "AttributeName": "Name", "AttributeType": "S" } ], "KeySchema": [ { "AttributeName": "Name", "KeyType": "HASH" } ], "BillingMode": "PAY_PER_REQUEST", "SSESpecification": { "Fn::If": [ "EncryptResourceData", { "KMSMasterKeyId": { "Ref": "ResourceEncryptionKey" }, "SSEEnabled": true, "SSEType": "KMS" }, { "KMSMasterKeyId": "", "SSEEnabled": false } ] } } }, "ConfigurationTable": { "Type": "AWS::DynamoDB::Table", "Properties": { "AttributeDefinitions": [ { "AttributeName": "Name", "AttributeType": "S" } ], "KeySchema": [ { "AttributeName": "Name", "KeyType": "HASH" } ], "BillingMode": "PAY_PER_REQUEST", "StreamSpecification": { "StreamViewType": "NEW_AND_OLD_IMAGES" }, "SSESpecification": { "Fn::If": [ "EncryptResourceData", { "KMSMasterKeyId": { "Ref": "ResourceEncryptionKey" }, "SSEEnabled": true, "SSEType": "KMS" }, { "KMSMasterKeyId": "", "SSEEnabled": false } ] } } }, "TaskTrackingTable": { "Type": "AWS::DynamoDB::Table", "Properties": { "AttributeDefinitions": [ { "AttributeName": "Id", "AttributeType": "S" }, { "AttributeName": "ConcurrencyId", "AttributeType": "S" }, { "AttributeName": "LastCompletionCheck", "AttributeType": "S" } ], "KeySchema": [ { "AttributeName": "Id", "KeyType": "HASH" } ], "SSESpecification": { "Fn::If": [ "EncryptResourceData", { "KMSMasterKeyId": { "Ref": "ResourceEncryptionKey" }, "SSEEnabled": true, "SSEType": "KMS" }, { "KMSMasterKeyId": "", "SSEEnabled": false } ] }, "BillingMode": "PAY_PER_REQUEST", "TimeToLiveSpecification": { "AttributeName": "TTL", "Enabled": { "Fn::If": [ "EnableTaskCleanupCondition", true, false ] } }, "StreamSpecification": { "StreamViewType": "NEW_AND_OLD_IMAGES" }, "GlobalSecondaryIndexes": [ { "IndexName": "WaitForExecutionTasks", "KeySchema": [ { "AttributeName": "ConcurrencyId", "KeyType": "HASH" }, { "AttributeName": "Id", "KeyType": "RANGE" } ], "Projection": { "ProjectionType": "ALL" } }, { "IndexName": "WaitForCompletionTasks", "KeySchema": [ { "AttributeName": "Id", "KeyType": "HASH" }, { "AttributeName": "LastCompletionCheck", "KeyType": "RANGE" } ], "Projection": { "ProjectionType": "ALL" } } ] } }, "ConcurrencyTable": { "Type": "AWS::DynamoDB::Table", "Properties": { "AttributeDefinitions": [ { "AttributeName": "ConcurrencyId", "AttributeType": "S" } ], "KeySchema": [ { "AttributeName": "ConcurrencyId", "KeyType": "HASH" } ], "BillingMode": "PAY_PER_REQUEST", "StreamSpecification": { "StreamViewType": "NEW_AND_OLD_IMAGES" }, "SSESpecification": { "Fn::If": [ "EncryptResourceData", { "KMSMasterKeyId": { "Ref": "ResourceEncryptionKey" }, "SSEEnabled": true, "SSEType": "KMS" }, { "KMSMasterKeyId": "", "SSEEnabled": false } ] } } }, "TriggerTable": { "Type": "AWS::DynamoDB::Table", "Properties": { "AttributeDefinitions": [ { "AttributeName": "Name", "AttributeType": "S" } ], "KeySchema": [ { "AttributeName": "Name", "KeyType": "HASH" } ], "BillingMode": "PAY_PER_REQUEST", "StreamSpecification": { "StreamViewType": "KEYS_ONLY" }, "SSESpecification": { "Fn::If": [ "EncryptResourceData", { "KMSMasterKeyId": { "Ref": "ResourceEncryptionKey" }, "SSEEnabled": true, "SSEType": "KMS" }, { "KMSMasterKeyId": "", "SSEEnabled": false } ] } } }, "OpsAutomatorLogGroup": { "Type": "AWS::Logs::LogGroup", "Properties": { "LogGroupName": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, "logs" ] ] }, "RetentionInDays": { "Ref": "LogRetentionDays" } } }, "OpsAutomatorIssuesTopic": { "Type": "AWS::SNS::Topic", "Properties": { "DisplayName": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "IssuesTopicDisplayName" ] } ] ] }, "TopicName": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "IssuesTopic" ] } ] ] }, "KmsMasterKeyId": { "Fn::If": [ "EncryptResourceData", { "Ref": "ResourceEncryptionKey" }, "" ] } } }, "OpsAutomatorNotificationsTopic": { "Type": "AWS::SNS::Topic", "Properties": { "DisplayName": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "NotificationsTopicDisplayName" ] } ] ] }, "TopicName": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "NotificationsTopic" ] } ] ] }, "KmsMasterKeyId": { "Fn::If": [ "EncryptResourceData", { "Ref": "ResourceEncryptionKey" }, "" ] } } }, "OpsAutomatorEventsTopic": { "Type": "AWS::SNS::Topic", "Properties": { "DisplayName": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "EventsTopicDisplayName" ] } ] ] }, "TopicName": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "EventsTopic" ] } ] ] }, "KmsMasterKeyId": { "Fn::If": [ "EncryptResourceData", { "Ref": "ResourceEncryptionKey" }, "" ] } } }, "OpsAutomatorEventsTopicPolicy": { "Type": "AWS::SNS::TopicPolicy", "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "F18", "reason": "* is used as Account pseudo variable cannot be used, access is restricted by condition" } ] } }, "Properties": { "Topics": [ { "Ref": "OpsAutomatorEventsTopic" } ], "PolicyDocument": { "Version": "2012-10-17", "Id": "default_policy", "Statement": [ { "Sid": "default_sid", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": [ "SNS:GetTopicAttributes", "SNS:SetTopicAttributes", "SNS:AddPermission", "SNS:RemovePermission", "SNS:DeleteTopic", "SNS:Subscribe", "SNS:ListSubscriptionsByTopic", "SNS:Publish", "SNS:Receive" ], "Resource": { "Ref": "OpsAutomatorEventsTopic" }, "Condition": { "StringEquals": { "AWS:SourceOwner": { "Ref": "AWS::AccountId" } } } }, { "Sid": "publish_sid_events", "Effect": "Allow", "Principal": { "Service": "events.amazonaws.com" }, "Action": "SNS:Publish", "Resource": { "Ref": "OpsAutomatorEventsTopic" } }, { "Sid": "publish_sid_s3", "Effect": "Allow", "Principal": { "Service": "s3.amazonaws.com" }, "Action": "SNS:Publish", "Resource": { "Ref": "OpsAutomatorEventsTopic" } }, { "Sid": "subscribe_sid", "Effect": "Allow", "Principal": { "AWS": { "Fn::Sub": "arn:aws:iam::${AWS::AccountId}:root" } }, "Action": [ "SNS:Subscribe", "SNS:Receive" ], "Resource": { "Ref": "OpsAutomatorEventsTopic" } } ] } } }, "OpsAutomatorLambdaFunctionStandard": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": { "Fn::Join": [ "-", [ "%bucket%", { "Ref": "AWS::Region" } ] ] }, "S3Key": "%solution%/v2.2.0/ops-automator.zip" }, "FunctionName": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "OpsAutomatorLambdaFunction" ] }, "Standard" ] ] }, "Handler": "main.lambda_handler", "Runtime": "python3.8", "Role": { "Fn::GetAtt": [ "OpsAutomatorLambdaRole", "Arn" ] }, "Environment": { "Variables": { "LAST_SCHEDULER_RUN_TABLE": { "Ref": "LastSchedulerExecutionTable" }, "RESOURCE_BUCKET": { "Ref": "TaskResources" }, "RESOURCE_TO_S3_SIZE": { "Fn::FindInMap": [ "Settings", "Resources", "ResourceToS3SizeKB" ] }, "ACTION_TRACKING_TABLE": { "Ref": "TaskTrackingTable" }, "CONFIG_TABLE": { "Ref": "ConfigurationTable" }, "CONFIG_BUCKET": { "Ref": "Configuration" }, "REPORTING_BUCKET": { "Ref": "Reporting" }, "CONCURRENCY_TABLE": { "Ref": "ConcurrencyTable" }, "TASKLIST_TAG_NAME": { "Ref": "TagName" }, "LOG_GROUP": { "Ref": "OpsAutomatorLogGroup" }, "CLOUDWATCH_TRIGGER_TABLE": { "Ref": "TriggerTable" }, "LOGGING_QUEUE_URL": { "Ref": "OpsAutomatorCloudWatchQueue" }, "LAMBDA_NAME": { "Fn::FindInMap": [ "Settings", "Names", "OpsAutomatorLambdaFunction" ] }, "STACK_NAME": { "Ref": "AWS::StackName" }, "STACK_ID": { "Ref": "AWS::StackId" }, "OPS_AUTOMATOR_ACCOUNT": { "Ref": "AWS::AccountId" }, "OPS_AUTOMATOR_ROLE_ARN": { "Fn::GetAtt": [ "OpsAutomatorLambdaRole", "Arn" ] }, "LAMBDA_TIMEOUT": "900", "TASK_CLEANUP_ENABLED": { "Fn::If": [ "EnableTaskCleanupCondition", "True", "False" ] }, "TASK_RETENTION_HOURS": { "Ref": "TaskRetentionHours" }, "KEEP_FAILED_TASKS": { "Fn::If": [ "KeepFailedTasksCondition", "True", "False" ] }, "USER_AGENT": { "Fn::Join": [ "-", [ "OpsAutomator", { "Ref": "AWS::StackName" }, "v2.2.0" ] ] }, "SEND_METRICS": { "Fn::FindInMap": [ "YesNoBoolean", { "Fn::FindInMap": [ "Send", "AnonymousUsage", "Data" ] }, "Value" ] }, "OPS_AUTOMATOR_RULE": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "OpsAutomatorRule" ] } ] ] }, "COMPLETION_RULE": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "OpsAutomatorCompletionRule" ] } ] ] }, "SNS_ISSUES_TOPIC_ARN": { "Ref": "OpsAutomatorIssuesTopic" }, "SNS_RESULT_TOPIC_ARN": { "Ref": "OpsAutomatorNotificationsTopic" }, "EVENTS_TOPIC_ARN": { "Ref": "OpsAutomatorEventsTopic" }, "BOTO_RETRY_STATS": "False", "BOTO_RETRY_OUTPUT": "False", "CLOUDWATCH_METRICS": { "Fn::FindInMap": [ "YesNoBoolean", { "Ref": "UseCloudWatchMetrics" }, "Value" ] }, "USE_ECS": { "Fn::If": [ "UseEcs", "True", "False" ] }, "ECS_CLUSTER": { "Fn::If": [ "UseEcs", { "Ref": "EcsCluster" }, { "Ref": "AWS::NoValue" } ] }, "ECS_OPS_AUTOMATOR_TASK": { "Fn::If": [ "UseEcs", { "Ref": "OpsAutomatorTask" }, "" ] }, "DEBUG_TASK_TRACKING_HANDLER": "False", "DEBUG_MAIN_EVENT_HANDLER": "False", "DEBUG_EVENT_HANDLERS": "False", "RESOURCE_ENCRYPTION_KEY": { "Fn::If": [ "EncryptResourceData", { "Ref": "ResourceEncryptionKey" }, "" ] }, "SERVICE_LIMIT_CONCURRENT_EBS_SNAPSHOT_COPY": { "Fn::FindInMap": [ "Settings", "ServiceLimits", "MaxConcurrentEbsSnapshotCopies" ] }, "SERVICE_LIMIT_CONCURRENT_RDS_SNAPSHOT_COPY": { "Fn::FindInMap": [ "Settings", "ServiceLimits", "MaxConcurrentRdsSnapshotCopies" ] }, "SERVICE_LIMIT_CONCURRENT_IMAGE_COPY": { "Fn::FindInMap": [ "Settings", "ServiceLimits", "MaxConcurrentImageCopies" ] }, "SOLUTION_ID": { "Fn::FindInMap": [ "Settings", "Metrics", "SolutionId" ] }, "METRICS_URL": { "Fn::FindInMap": [ "Settings", "Metrics", "Url" ] }, "AWSVPC_SUBNETS": { "Fn::If": [ "UseEcs", { "Fn::If": [ "CreateVpcResources", { "Fn::Join": [ ",", [ { "Ref": "PublicSubnetAz1" }, { "Ref": "PublicSubnetAz2" } ] ] }, { "Fn::Join": [ ",", { "Ref": "SubnetIds" } ] } ] }, "None" ] }, "AWSVPC_SECURITYGROUPS": { "Fn::If": [ "UseEcs", { "Ref": "FargateSecurityGroup" }, "None" ] }, "AWSVPC_ASSIGNPUBLICIP": "ENABLED" } }, "MemorySize": { "Fn::FindInMap": [ "Settings", "ActionMemory", "Standard" ] }, "Timeout": 900, "Description": "Ops Automator %size%, version v2.2.0" } }, "OpsAutomatorCloudWatchQueueHandlerLambda": { "Type": "AWS::Lambda::Function", "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W48", "reason": "KMS not required: no customer data" } ] } }, "Properties": { "Code": { "S3Bucket": { "Fn::Join": [ "-", [ "%bucket%", { "Ref": "AWS::Region" } ] ] }, "S3Key": "%solution%/v2.2.0/cloudwatch-handler.zip" }, "FunctionName": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "CloudWatchQueueHandlerLambdaFunction" ] } ] ] }, "Handler": "cloudwatch_queue_handler_lambda.lambda_handler", "Runtime": "python3.8", "Role": { "Fn::GetAtt": [ "OpsAutomatorCloudWatchLogHandlerRole", "Arn" ] }, "Environment": { "Variables": { "CWL_LIMIT_PUT_CALLS_PER_STREAM": { "Fn::FindInMap": [ "Settings", "CloudWatchApiLimits", "MaxPutCallsPerStream" ] }, "CWL_LIMIT_PUT_CALLS_PER_ACCOUNT": { "Fn::If": [ "CloudWatchPutLimit800", 800, 1500 ] }, "CWL_LIMIT_API_CALLS": { "Fn::FindInMap": [ "Settings", "CloudWatchApiLimits", "MaxApiCalls" ] }, "LOGGING_QUEUE_URL": { "Ref": "OpsAutomatorCloudWatchQueue" }, "CLOUDWATCH_TRIGGER_TABLE": { "Ref": "TriggerTable" }, "LOG_GROUP": { "Ref": "OpsAutomatorLogGroup" } } }, "MemorySize": 128, "Timeout": 900, "Description": "Ops Automator CloudWatch Queue Handler, version v2.2.0" } }, "OpsAutomatorEventsTopicSubscription": { "Type": "AWS::SNS::Subscription", "Properties": { "Endpoint": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] }, "Protocol": "lambda", "TopicArn": { "Ref": "OpsAutomatorEventsTopic" } } }, "OpsAutomatorTask": { "Type": "AWS::ECS::TaskDefinition", "Condition": "UseEcs", "Properties": { "Family": { "Fn::Sub": "${AWS::StackName}-ops-automator-task" }, "ContainerDefinitions": [ { "Essential": true, "Environment": [ { "Name": "IS_ECS_JOB", "Value": "True" }, { "Name": "SUPPRESS_LOG_TO_STDOUT", "Value": "True" } ], "LogConfiguration": { "LogDriver": "awslogs", "Options": { "awslogs-group": { "Ref": "OpsAutomatorLogGroup" }, "awslogs-stream-prefix": "Ecs", "awslogs-region": { "Ref": "AWS::Region" } } }, "Name": "ops-automator", "Image": { "Fn::Sub": [ "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${repository}:latest", { "repository": { "Ref": "OpsAutomatorEcsRepository" } } ] }, "MemoryReservation": 128 } ], "Cpu": "1024", "Memory": "2048", "RequiresCompatibilities": [ "FARGATE" ], "NetworkMode": "awsvpc", "TaskRoleArn": { "Fn::GetAtt": [ "OpsAutomatorLambdaRole", "Arn" ] }, "ExecutionRoleArn": { "Ref": "EcsExecutionRole" }, "Volumes": [] } }, "Configuration": { "Type": "AWS::S3::Bucket", "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W51", "reason": "The bucket is not public. When using the CF template in PROD, create a bucket policy to allow only administrators/ auditors access to the bucket" } ] } }, "DeletionPolicy": "Retain", "Properties": { "BucketName": { "Fn::If": [ "CustomConfigBucketName", { "Fn::FindInMap": [ "Settings", "Names", "ConfigurationBucket" ] }, { "Ref": "AWS::NoValue" } ] }, "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { "ServerSideEncryptionByDefault": { "SSEAlgorithm": "AES256" } } ] }, "LifecycleConfiguration": { "Rules": [ { "ExpirationInDays": { "Ref": "ConfigBackupDays" }, "Prefix": "Backups/", "Status": "Enabled" } ] }, "AccessControl": "Private", "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, "LoggingConfiguration": { "DestinationBucketName": { "Ref": "S3LoggingBucket" }, "LogFilePrefix": "access-logs" } } }, "Reporting": { "Type": "AWS::S3::Bucket", "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W51", "reason": "The bucket is not public. When using the CF template in PROD, create a bucket policy to allow only administrators/ auditors access to the bucket" } ] } }, "DeletionPolicy": "Retain", "Properties": { "BucketName": { "Fn::If": [ "CustomReportingBucketName", { "Fn::FindInMap": [ "Settings", "Names", "ReportingBucket" ] }, { "Ref": "AWS::NoValue" } ] }, "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { "ServerSideEncryptionByDefault": { "SSEAlgorithm": "AES256" } } ] }, "AccessControl": "Private", "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, "LoggingConfiguration": { "DestinationBucketName": { "Ref": "S3LoggingBucket" }, "LogFilePrefix": "access-logs" } } }, "OpsAutomatorCloudWatchQueue": { "Type": "AWS::SQS::Queue", "Properties": { "FifoQueue": { "Fn::If": [ "FifoQueue", true, { "Ref": "AWS::NoValue" } ] }, "MessageRetentionPeriod": 86400, "ReceiveMessageWaitTimeSeconds": 0, "VisibilityTimeout": 300, "QueueName": { "Fn::Join": [ "", [ { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "LogQueueName" ] } ] ] }, { "Fn::If": [ "FifoQueue", ".fifo", "" ] } ] ] }, "KmsMasterKeyId": { "Fn::If": [ "EncryptResourceData", { "Ref": "ResourceEncryptionKey" }, "" ] } } }, "OpsAutomatorCloudWatchQueuePolicy": { "Type": "AWS::SQS::QueuePolicy", "Properties": { "PolicyDocument": { "Id": "OpsAutomatorPutPolicy", "Version": "2012-10-17", "Statement": [ { "Sid": "AllowSendMessage", "Effect": "Allow", "Principal": { "AWS": { "Fn::GetAtt": [ "OpsAutomatorLambdaRole", "Arn" ] } }, "Action": [ "sqs:SendMessage" ], "Resource": [ { "Fn::GetAtt": [ "OpsAutomatorCloudWatchQueue", "Arn" ] } ] }, { "Sid": "AllowReceiveMessage", "Effect": "Allow", "Principal": { "AWS": { "Fn::GetAtt": [ "OpsAutomatorCloudWatchLogHandlerRole", "Arn" ] } }, "Action": [ "sqs:DeleteMessage", "sqs:ReceiveMessage" ], "Resource": [ { "Fn::GetAtt": [ "OpsAutomatorCloudWatchQueue", "Arn" ] } ] } ] }, "Queues": [ { "Ref": "OpsAutomatorCloudWatchQueue" } ] } }, "TaskResources": { "Type": "AWS::S3::Bucket", "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W51", "reason": "The bucket is not public. When using the CF template in PROD, create a bucket policy to allow only administrators/ auditors access to the bucket" } ] } }, "DeletionPolicy": "Retain", "Properties": { "BucketName": { "Fn::If": [ "CustomResourcesBucketName", { "Fn::FindInMap": [ "Settings", "Names", "ResourcesBucket" ] }, { "Ref": "AWS::NoValue" } ] }, "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { "ServerSideEncryptionByDefault": { "SSEAlgorithm": "AES256" } } ] }, "AccessControl": "Private", "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, "IgnorePublicAcls": true, "RestrictPublicBuckets": true }, "LoggingConfiguration": { "DestinationBucketName": { "Ref": "S3LoggingBucket" }, "LogFilePrefix": "access-logs" } } }, "OpsAutomatorInvokePermissionDynamodb": { "Type": "AWS::Lambda::Permission", "Properties": { "FunctionName": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] }, "Action": "lambda:InvokeFunction", "Principal": "dynamodb.amazonaws.com" } }, "CloudWatchHandlerInvokePermissionDynamodb": { "Type": "AWS::Lambda::Permission", "Properties": { "FunctionName": { "Fn::GetAtt": [ "OpsAutomatorCloudWatchQueueHandlerLambda", "Arn" ] }, "Action": "lambda:InvokeFunction", "Principal": "dynamodb.amazonaws.com" } }, "InvokePermissionSNS": { "Type": "AWS::Lambda::Permission", "Properties": { "FunctionName": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] }, "Action": "lambda:InvokeFunction", "Principal": "sns.amazonaws.com", "SourceArn": { "Ref": "OpsAutomatorEventsTopic" } } }, "InvokePermissionCloudWatchEvents": { "Type": "AWS::Lambda::Permission", "Properties": { "FunctionName": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] }, "Action": "lambda:InvokeFunction", "Principal": "events.amazonaws.com", "SourceArn": { "Fn::GetAtt": [ "OpsAutomatorRule", "Arn" ] } } }, "InvokePermissionCloudWatchEventsCompletion": { "Type": "AWS::Lambda::Permission", "Properties": { "FunctionName": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] }, "Action": "lambda:InvokeFunction", "Principal": "events.amazonaws.com", "SourceArn": { "Fn::GetAtt": [ "OpsAutomatorCompletionRule", "Arn" ] } } }, "Ec2StateEventRule": { "Type": "AWS::Events::Rule", "Properties": { "Description": { "Fn::Sub": "Ops Automator - Rule for CloudWatch EC2 events for stack ${AWS::StackName}" }, "Name": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "Ec2StateEventRule" ] } ] ] }, "EventPattern": { "source": [ "aws.ec2" ], "detail-type": [ "EC2 Instance State-change Notification" ], "detail": { "state": [ "running", "stopped", "terminated" ] } }, "Targets": [ { "Arn": { "Ref": "OpsAutomatorEventsTopic" }, "Id": "EC2StateEventsToTopic" } ] } }, "EbsSnapshotEventRule": { "Type": "AWS::Events::Rule", "Properties": { "Description": { "Fn::Sub": "Ops Automator - Rule for CloudWatch EBS events for stack ${AWS::StackName}" }, "Name": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "EbsSnapshotEventRule" ] } ] ] }, "EventPattern": { "source": [ "aws.ec2" ], "detail-type": [ "EBS Snapshot Notification" ], "detail": { "event": [ "copySnapshot", "createSnapshot", "shareSnapshot" ], "result": [ "succeeded" ] } }, "Targets": [ { "Arn": { "Ref": "OpsAutomatorEventsTopic" }, "Id": "EbsSnapshotEventsToTopic" } ] } }, "CloudTrailRdsEventRule": { "Type": "AWS::Events::Rule", "Properties": { "Description": { "Fn::Sub": "Ops Automator - Rule for CloudTrail events for stack ${AWS::StackName}" }, "Name": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "CloudTrailRdsEventRule" ] } ] ] }, "EventPattern": { "source": [ "aws.rds" ], "detail-type": [ "AWS API Call via CloudTrail" ], "detail": { "eventSource": [ "rds.amazonaws.com" ], "eventName": [ "CreateDBCluster", "CreateDBInstance", "DeleteDBCluster", "DeleteDBInstance", "RestoreDBClusterSnapshot", "RestoreDBInstanceFromDBSnapshot", "StartDBCluster", "StartDBInstance", "StartDBInstance", "StopDBCluster", "StopDBInstance" ] } }, "Targets": [ { "Arn": { "Ref": "OpsAutomatorEventsTopic" }, "Id": "CloudTrailRdsEventsToTopic" } ] } }, "TagChangeEventsRule": { "Type": "AWS::Events::Rule", "Properties": { "Description": { "Fn::Sub": "Ops Automator - Rule for tag change events for stack ${AWS::StackName}" }, "Name": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "TaggingEventRule" ] } ] ] }, "EventPattern": { "source": [ "aws.tag" ], "detail-type": [ "Tag Change on Resource" ], "detail": { "service": [ "ec2", "rds" ], "resource-type": [ "instance", "volume", "snapshot", "image", "db", "cluster" ] } }, "Targets": [ { "Arn": { "Ref": "OpsAutomatorEventsTopic" }, "Id": "TagChangeEventsToTopic" } ] } }, "OpsAutomatorExportTask": { "Type": "Custom::TaskConfig", "Properties": { "ServiceToken": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] }, "Timeout": "300", "Name": "TaskTableExport", "Description": "Internal task for exporting task tracking table to S3, do not delete", "Parameters": { "S3Bucket": { "Ref": "Configuration" }, "S3Prefix": "Exports/" }, "Interval": "59 0/4 * * ?", "Internal": "True", "Action": "SchedulerTaskExport", "TaskMetrics": "False", "Enabled": { "Fn::If": [ "EnableTaskExportCondition", "True", "False" ] }, "StackId": { "Ref": "AWS::StackId" } } }, "OpsAutomatorCleanupTask": { "Type": "Custom::TaskConfig", "Properties": { "ServiceToken": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] }, "Timeout": "300", "Name": "TaskTableCleanup", "Description": "Internal task for cleaning task tracking table, do not delete. Deletes task not deleted by TTL", "Parameters": { "TaskRetentionHours": { "Ref": "TaskRetentionHours" }, "RetainFailedTasks": { "Fn::If": [ "KeepFailedTasksCondition", "True", "False" ] } }, "Interval": "0 2 * * ?", "Internal": "True", "TaskMetrics": "False", "Action": "SchedulerTaskCleanup", "Enabled": { "Fn::If": [ "EnableTaskCleanupCondition", "True", "False" ] }, "StackId": { "Ref": "AWS::StackId" } }, "DependsOn": [ "OpsAutomatorExportTask" ] }, "OpsAutomatorBackupTask": { "Type": "Custom::TaskConfig", "Properties": { "ServiceToken": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] }, "Timeout": "300", "Name": "TaskConfigurationBackup", "Description": "Internal task for daily backup of configuration table, do not delete", "Parameters": { "S3Bucket": { "Ref": "Configuration" }, "S3Prefix": "Backups/" }, "Interval": "0 0 * * ?", "Internal": "True", "TaskMetrics": "False", "Action": "SchedulerConfigBackup", "Enabled": "True", "StackId": { "Ref": "AWS::StackId" } }, "DependsOn": [ "OpsAutomatorCleanupTask" ] }, "OpsAutomatorSetupHelper": { "Type": "Custom::OpsAutomatorSetupHelper", "Properties": { "ServiceToken": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] }, "UseEcs": { "Fn::If": [ "UseEcs", "True", "False" ] }, "Timeout": "150", "LogRetentionDays": { "Ref": "LogRetentionDays" }, "StackVersion": "v2.2.0", "OpsAutomatorLambdaRole": { "Fn::GetAtt": [ "OpsAutomatorLambdaRole", "Arn" ] }, "OptimizeCrossAccountTemplate": { "Fn::FindInMap": [ "Settings", "Templates", "OptimizeCrossAccountTemplate" ] }, "EventForwardLambdaRole": { "Fn::GetAtt": [ "OpsAutomatorEventsForwardRole", "Arn" ] }, "OpsAutomatorTopicArn": { "Ref": "OpsAutomatorEventsTopic" }, "DeploymentVersion": "v2.2.0" }, "DependsOn": [ "ConfigurationTable", "TriggerTable", "OpsAutomatorCloudWatchQueueHandlerLambda" ] }, "OpsAutomatorRule": { "Type": "AWS::Events::Rule", "Properties": { "Description": { "Fn::Sub": "Ops Automator - Rule to trigger scheduled tasks for stack ${AWS::StackName}" }, "Name": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "OpsAutomatorRule" ] } ] ] }, "ScheduleExpression": "cron(0/1 * * * ? *)", "State": { "Fn::FindInMap": [ "EnabledDisabled", { "Ref": "SchedulerActive" }, "Value" ] }, "Targets": [ { "Id": "OpsAutomator", "Arn": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] } } ] } }, "OpsAutomatorCompletionRule": { "Type": "AWS::Events::Rule", "Properties": { "Description": { "Fn::Sub": "Ops Automator - Rule to trigger completion checks for stack ${AWS::StackName}" }, "Name": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, { "Fn::FindInMap": [ "Settings", "Names", "OpsAutomatorCompletionRule" ] } ] ] }, "ScheduleExpression": "cron(0/1 * * * ? *)", "State": "ENABLED", "Targets": [ { "Id": "OpsAutomatorCompletion", "Arn": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] } } ] } }, "ActionEventMappingTaskTracking": { "Type": "AWS::Lambda::EventSourceMapping", "Properties": { "BatchSize": 20, "Enabled": true, "EventSourceArn": { "Fn::GetAtt": [ "TaskTrackingTable", "StreamArn" ] }, "FunctionName": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] }, "StartingPosition": "TRIM_HORIZON" }, "DependsOn": "OpsAutomatorLambdaRole" }, "ActionEventMappingConcurrencyTable": { "Type": "AWS::Lambda::EventSourceMapping", "Properties": { "BatchSize": 1, "Enabled": true, "EventSourceArn": { "Fn::GetAtt": [ "ConcurrencyTable", "StreamArn" ] }, "FunctionName": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] }, "StartingPosition": "TRIM_HORIZON" }, "DependsOn": "OpsAutomatorLambdaRole" }, "ActionEventMappingConfiguration": { "Type": "AWS::Lambda::EventSourceMapping", "Properties": { "BatchSize": 20, "Enabled": true, "EventSourceArn": { "Fn::GetAtt": [ "ConfigurationTable", "StreamArn" ] }, "FunctionName": { "Fn::GetAtt": [ "OpsAutomatorLambdaFunctionStandard", "Arn" ] }, "StartingPosition": "TRIM_HORIZON" } }, "ActionEventMappingTriggerTable": { "Type": "AWS::Lambda::EventSourceMapping", "Properties": { "Enabled": true, "EventSourceArn": { "Fn::GetAtt": [ "TriggerTable", "StreamArn" ] }, "FunctionName": { "Fn::GetAtt": [ "OpsAutomatorCloudWatchQueueHandlerLambda", "Arn" ] }, "StartingPosition": "TRIM_HORIZON" } }, "OpsAutomatorEcsRepository": { "Condition": "UseEcs", "Type": "AWS::ECR::Repository", "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W28", "reason": "Dynamic names for ECS do not follow standard practice for resource naming and are not deriveable from stackname." } ] } }, "DeletionPolicy": "Retain", "Properties": { "RepositoryName": "ops-automator" } }, "ResourceEncryptionKey": { "Type": "AWS::KMS::Key", "Properties": { "Description": "KMS key for encrypting Ops Automator resource data", "EnableKeyRotation": true, "KeyPolicy": { "Version": "2012-10-17", "Id": "key-default-1", "Statement": [ { "Sid": "Enable IAM User Permissions", "Effect": "Allow", "Principal": { "AWS": { "Fn::Join": [ "", [ "arn:aws:iam::", { "Ref": "AWS::AccountId" }, ":root" ] ] } }, "Action": "kms:*", "Resource": "*" } ] } }, "Condition": "EncryptResourceData" }, "ResourceEncryptionKeyAlias": { "Type": "AWS::KMS::Alias", "Properties": { "AliasName": { "Fn::Sub": "alias/${AWS::StackName}-ResourceEncryptionKey" }, "TargetKeyId": { "Ref": "ResourceEncryptionKey" } }, "Condition": "EncryptResourceData" }, "S3LoggingBucket": { "DeletionPolicy": "Retain", "Type": "AWS::S3::Bucket", "Properties": { "BucketName": { "Fn::Sub": "aws-opsautomator-s3-access-logs-${AWS::AccountId}-${AWS::Region}" }, "AccessControl": "LogDeliveryWrite", "VersioningConfiguration": { "Status": "Enabled" }, "BucketEncryption": { "ServerSideEncryptionConfiguration": [ { "ServerSideEncryptionByDefault": { "SSEAlgorithm": "AES256" } } ] }, "Tags": [ { "Key": "Name", "Value": "AWS Ops Automator Access Logs" } ], "PublicAccessBlockConfiguration": { "BlockPublicAcls": true, "BlockPublicPolicy": true, "IgnorePublicAcls": true, "RestrictPublicBuckets": true } }, "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W35", "reason": "This S3 bucket is used as the destination for storing access logs" }, { "id": "W51", "reason": "Log delivery controlled by ACL, not bucket policy" } ] } } }, "EcsCluster": { "Condition": "UseEcs", "Type": "AWS::ECS::Cluster" }, "Vpc": { "Condition": "CreateVpcResources", "Type": "AWS::EC2::VPC", "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W60", "reason": "VPC is only used for the Fargate cluster, reducing the importance of Flow Logs (which add cost)." } ] } }, "Properties": { "CidrBlock": { "Fn::FindInMap": [ "Settings", "VpcCidrs", "vpc" ] }, "Tags": [ { "Key": "Name", "Value": { "Fn::Join": [ "-", [ { "Ref": "AWS::StackName" }, "vpc" ] ] } } ] } }, "PublicSubnetAz1": { "Condition": "CreateVpcResources", "Type": "AWS::EC2::Subnet", "Properties": { "VpcId": { "Ref": "Vpc" }, "CidrBlock": { "Fn::FindInMap": [ "Settings", "VpcCidrs", "pubsubnet1" ] }, "AvailabilityZone": { "Fn::If": [ "UseSpecifiedVpcAvailabilityZones", { "Fn::Select": [ "0", { "Ref": "VpcAvailabilityZones" } ] }, { "Fn::Select": [ "0", { "Fn::GetAZs": { "Ref": "AWS::Region" } } ] } ] } } }, "PublicSubnetAz2": { "Condition": "CreateVpcResources", "Type": "AWS::EC2::Subnet", "Properties": { "VpcId": { "Ref": "Vpc" }, "CidrBlock": { "Fn::FindInMap": [ "Settings", "VpcCidrs", "pubsubnet2" ] }, "AvailabilityZone": { "Fn::If": [ "UseSpecifiedVpcAvailabilityZones", { "Fn::Select": [ "1", { "Ref": "VpcAvailabilityZones" } ] }, { "Fn::Select": [ "1", { "Fn::GetAZs": { "Ref": "AWS::Region" } } ] } ] } } }, "InternetGateway": { "Condition": "CreateVpcResources", "Type": "AWS::EC2::InternetGateway" }, "AttachGateway": { "Condition": "CreateVpcResources", "Type": "AWS::EC2::VPCGatewayAttachment", "Properties": { "VpcId": { "Ref": "Vpc" }, "InternetGatewayId": { "Ref": "InternetGateway" } } }, "RouteViaIgw": { "Condition": "CreateVpcResources", "Type": "AWS::EC2::RouteTable", "Properties": { "VpcId": { "Ref": "Vpc" } } }, "PublicRouteViaIgw": { "Condition": "CreateVpcResources", "DependsOn": "AttachGateway", "Type": "AWS::EC2::Route", "Properties": { "RouteTableId": { "Ref": "RouteViaIgw" }, "DestinationCidrBlock": "0.0.0.0/0", "GatewayId": { "Ref": "InternetGateway" } } }, "PubSubnet1RouteTableAssociation": { "Condition": "CreateVpcResources", "Type": "AWS::EC2::SubnetRouteTableAssociation", "Properties": { "SubnetId": { "Ref": "PublicSubnetAz1" }, "RouteTableId": { "Ref": "RouteViaIgw" } } }, "PubSubnet2RouteTableAssociation": { "Condition": "CreateVpcResources", "Type": "AWS::EC2::SubnetRouteTableAssociation", "Properties": { "SubnetId": { "Ref": "PublicSubnetAz2" }, "RouteTableId": { "Ref": "RouteViaIgw" } } }, "FargateSecurityGroup": { "Condition": "UseEcs", "Type": "AWS::EC2::SecurityGroup", "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W40", "reason": "Must allow egress of all protocols, all dests." }, { "id": "W5", "reason": "Must allow egress of all protocols, all dests." } ] } }, "Description": "Security group for Fargate cluster", "Properties": { "GroupDescription": "Access to the Fargate containers", "VpcId": { "Fn::If": [ "CreateVpcResources", { "Ref": "Vpc" }, { "Ref": "VpcId" } ] } } }, "FargateSGSelfEgress": { "Condition": "UseEcs", "Type": "AWS::EC2::SecurityGroupEgress", "Properties": { "Description": "Egress to other containers in the same security group", "GroupId": { "Ref": "FargateSecurityGroup" }, "IpProtocol": -1, "FromPort": -1, "ToPort": -1, "SourceSecurityGroupId": { "Ref": "FargateSecurityGroup" } } }, "FargateSGExtEgress": { "Condition": "UseEcs", "Type": "AWS::EC2::SecurityGroupEgress", "Properties": { "Description": "Egress to external networks", "GroupId": { "Ref": "FargateSecurityGroup" }, "IpProtocol": -1, "FromPort": -1, "ToPort": -1, "CidrIp": "0.0.0.0/0" } }, "ECSRole": { "Condition": "UseEcs", "Type": "AWS::IAM::Role", "Metadata": { "cfn_nag": { "rules_to_suppress": [ { "id": "W11", "reason": "Global access to services required for automation to function" } ] } }, "Properties": { "AssumeRolePolicyDocument": { "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "ecs.amazonaws.com" ] }, "Action": [ "sts:AssumeRole" ] } ] }, "Path": "/", "Policies": [ { "PolicyName": "ecs-service", "PolicyDocument": { "Statement": [ { "Effect": "Allow", "Action": [ "ec2:AttachNetworkInterface", "ec2:CreateNetworkInterface", "ec2:CreateNetworkInterfacePermission", "ec2:DeleteNetworkInterface", "ec2:DeleteNetworkInterfacePermission", "ec2:Describe*", "ec2:DetachNetworkInterface", "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", "elasticloadbalancing:DeregisterTargets", "elasticloadbalancing:Describe*", "elasticloadbalancing:RegisterInstancesWithLoadBalancer", "elasticloadbalancing:RegisterTargets" ], "Resource": "*" }, { "Sid": "KMS", "Effect": "Allow", "Resource": { "Fn::GetAtt": [ "ResourceEncryptionKey", "Arn" ] }, "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:DescribeKey" ] } ] } } ] } } }, "Outputs": { "OpsAutomatorLambdaFunctionStandard": { "Value": { "Ref": "OpsAutomatorLambdaFunctionStandard" }, "Description": "Name of Ops Automator lambda function" }, "LogGroup": { "Value": { "Ref": "OpsAutomatorLogGroup" }, "Description": "Name of the CloudWatch loggroup for Ops Automator stack" }, "ConfigurationBucketName": { "Value": { "Ref": "Configuration" }, "Description": "Name of bucket with generated CloudFormation templates for managing tasks and creating cross account roles and backups" }, "ReportingBucketName": { "Value": { "Ref": "Reporting" }, "Description": "Name of bucket for output reports of Ops Automator tasks" }, "IssueSNSTopic": { "Value": { "Ref": "OpsAutomatorIssuesTopic" }, "Description": "Topic to subscribe to for notifications of errors and warnings" }, "NotificationsSNSTopic": { "Value": { "Ref": "OpsAutomatorNotificationsTopic" }, "Description": "Topic to subscribe to for notifications of started and ended tasks" }, "StackId": { "Value": { "Ref": "AWS::StackId" }, "Description": "StackID" }, "EventsForwardRoleParam": { "Value": { "Fn::GetAtt": [ "OpsAutomatorEventsForwardRole", "Arn" ] }, "Description": "Role for forwarding events from other regions" }, "Repository": { "Value": { "Fn::If": [ "UseEcs", { "Fn::Sub": [ "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${repository}", { "repository": { "Ref": "OpsAutomatorEcsRepository" } } ] }, "" ] } }, "Cluster": { "Value": { "Fn::If": [ "UseEcs", { "Ref": "EcsCluster" }, "" ] } } } }