AWSTemplateFormatVersion: 2010-09-09 Description: Deploy temporary CloudTrail stacks using CloudFormation stackset Metadata: 'AWS::CloudFormation::Interface': ParameterGroups: - Label: default: 'AWS CloudTrail Configuration' Parameters: - OrganizationRootID - ManagedResourcePrefix - AuditBucketName - AWSLogsS3KeyPrefix - LogsRetentionInDays - EnableLogFileValidation - IncludeGlobalEvents - MultiRegion - PublishToCloudWatchLogs - KMSKeyArn Parameters: OrganizationRootID: Type: String MaxLength: 36 MinLength: 6 Description: The unique identifier (ID) of an organization's root (r-abcd) AllowedPattern: ^r-[0-9a-z]{4,32}$ AuditBucketName: Type: String Description: Audit Bucket name from the Log Archive Account AWSLogsS3KeyPrefix: Type: 'String' Description: 'Organization ID to use as the S3 Key prefix for storing the audit logs' KMSKeyArn: Type: 'String' Description: 'KMS key ARN for enabling KMS encryption.' Default: '' LogsRetentionInDays: Description: 'Specifies the number of days you want to retain CloudTrail log events in the CloudWatch Logs.' Type: Number Default: 14 AllowedValues: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653] ManagedResourcePrefix: Type: 'String' Description: 'Prefix for the managed resources' Default: "temporary-trail" EnableLogFileValidation: Type: String Default: 'true' Description: Indicates whether CloudTrail validates the integrity of log files. AllowedValues: - 'true' - 'false' IncludeGlobalEvents: Type: String Default: 'true' Description: Indicates whether the trail is publishing events from global services, such as IAM, to the log files. AllowedValues: - 'true' - 'false' MultiRegion: Type: String Default: 'true' Description: Indicates whether the CloudTrail trail is created in the region in which you create the stack (false) or in all regions (true). AllowedValues: - 'true' - 'false' PublishToCloudWatchLogs: Type: String Default: 'true' Description: Indicates whether notifications are published to CloudWatch Logs. AllowedValues: - 'true' - 'false' Resources: TempCTAdministrationRole: DependsOn: ExecutionRole Type: AWS::IAM::Role Properties: RoleName: TempCTStackSetAdministrationRole AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: cloudformation.amazonaws.com Action: - sts:AssumeRole Path: / Policies: - PolicyName: AssumeRole-TempCTStackSetExecutionRole PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - sts:AssumeRole Resource: - "arn:aws:iam::*:role/TempCTStackSetExecutionRole" ExecutionRole: Type: AWS::IAM::Role Properties: RoleName: TempCTStackSetExecutionRole AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: AWS: - !Ref "AWS::AccountId" Action: - sts:AssumeRole Path: / Policies: - PolicyName: ADDITIONAL_PERM PolicyDocument: Version: '2012-10-17' Statement: Sid: WritePermissions Effect: Allow Action: - iam:CreateRole - iam:DeleteRole - iam:DeleteRolePolicy - iam:PutRolePolicy - iam:DetachRolePolicy - iam:AttachRolePolicy Resource: !Join ["", ["arn:", !Ref AWS::Partition, ":iam::", !Ref AWS::AccountId, ":role/*-CloudWatchLogsRole"]] ManagedPolicyArns: - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess - arn:aws:iam::aws:policy/AWSCloudTrail_FullAccess - arn:aws:iam::aws:policy/IAMReadOnlyAccess - arn:aws:iam::aws:policy/AWSCloudFormationFullAccess - arn:aws:iam::aws:policy/AmazonSNSFullAccess CrossAccountAccessRole: Type: AWS::CloudFormation::StackSet Properties: Description: StackSet for creating CloudTrail cross account StackSetName: CustomCloudTrailCreateRole PermissionModel: SERVICE_MANAGED AutoDeployment: Enabled: true RetainStacksOnAccountRemoval: false OperationPreferences: FailureTolerancePercentage: 100 MaxConcurrentPercentage: 100 Parameters: - ParameterKey: AdministratorAccountId ParameterValue: !Ref "AWS::AccountId" StackInstancesGroup: - Regions: - !Ref "AWS::Region" DeploymentTargets: OrganizationalUnitIds: - !Ref OrganizationRootID Capabilities: - CAPABILITY_NAMED_IAM TemplateBody: | AWSTemplateFormatVersion: 2010-09-09 Description: Required role to enable AWS CloudFormation StackSets. Parameters: AdministratorAccountId: Type: String Description: AWS Account Id of the administrator account (the account in which StackSets will be created). MaxLength: 12 MinLength: 12 Resources: ExecutionRole: Type: AWS::IAM::Role Properties: RoleName: TempCTStackSetExecutionRole AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: AWS: - !Ref AdministratorAccountId Action: - sts:AssumeRole Path: / Policies: - PolicyName: ADDITIONAL_PERM PolicyDocument: Version: '2012-10-17' Statement: Sid: WritePermissions Effect: Allow Action: - iam:CreateRole - iam:DeleteRole - iam:DeleteRolePolicy - iam:PutRolePolicy - iam:DetachRolePolicy - iam:AttachRolePolicy Resource: !Join ["", ["arn:", !Ref AWS::Partition, ":iam::", !Ref AWS::AccountId, ":role/*-CloudWatchLogsRole"]] ManagedPolicyArns: - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess - arn:aws:iam::aws:policy/AWSCloudTrail_FullAccess - arn:aws:iam::aws:policy/IAMReadOnlyAccess - arn:aws:iam::aws:policy/AWSCloudFormationFullAccess - arn:aws:iam::aws:policy/AmazonSNSFullAccess TemporaryCloudTrailStackSet: Type: AWS::CloudFormation::StackSet DependsOn: CrossAccountAccessRole Properties: Description: StackSet to create Temporary CloudTrail StackSetName: TemporaryCloudTrailStackSet PermissionModel: SELF_MANAGED ExecutionRoleName: TempCTStackSetExecutionRole AdministrationRoleARN: !GetAtt TempCTAdministrationRole.Arn StackInstancesGroup: - Regions: - !Ref "AWS::Region" DeploymentTargets: Accounts: !GetAtt GetAccountList.Data OperationPreferences: FailureTolerancePercentage: 100 MaxConcurrentPercentage: 100 Parameters: - ParameterKey: AuditBucketName ParameterValue: !Ref AuditBucketName - ParameterKey: KMSKeyArn ParameterValue: !Ref KMSKeyArn - ParameterKey: AWSLogsS3KeyPrefix ParameterValue: !Ref AWSLogsS3KeyPrefix - ParameterKey: ManagedResourcePrefix ParameterValue: !Ref ManagedResourcePrefix - ParameterKey: EnableLogFileValidation ParameterValue: !Ref EnableLogFileValidation - ParameterKey: IncludeGlobalEvents ParameterValue: !Ref IncludeGlobalEvents - ParameterKey: MultiRegion ParameterValue: !Ref MultiRegion - ParameterKey: PublishToCloudWatchLogs ParameterValue: !Ref PublishToCloudWatchLogs - ParameterKey: LogsRetentionInDays ParameterValue: !Ref LogsRetentionInDays Capabilities: - CAPABILITY_NAMED_IAM TemplateBody: | AWSTemplateFormatVersion: 2010-09-09 Description: Configure temporary AWS CloudTrail Parameters: ManagedResourcePrefix: Type: 'String' Description: 'Prefix for the managed resources' Default: "temporary-trail" EnableLogFileValidation: Type: String Default: 'true' Description: Indicates whether CloudTrail validates the integrity of log files. AllowedValues: - 'true' - 'false' IncludeGlobalEvents: Type: String Default: 'true' Description: Indicates whether the trail is publishing events from global services, such as IAM, to the log files. AllowedValues: - 'true' - 'false' MultiRegion: Type: String Default: 'true' Description: Indicates whether the CloudTrail trail is created in the region in which you create the stack (false) or in all regions (true). AllowedValues: - 'true' - 'false' AuditBucketName: Type: String Default: '' Description: Audit Bucket name from the Log Archive Account PublishToCloudWatchLogs: Type: String Default: 'true' Description: Indicates whether notifications are published to CloudWatch Logs. AllowedValues: - 'true' - 'false' LogsRetentionInDays: Description: 'Specifies the number of days you want to retain CloudTrail log events in the CloudWatch Logs.' Type: Number Default: 14 AllowedValues: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653] AWSLogsS3KeyPrefix: Type: 'String' Description: 'Organization ID to use as the S3 Key prefix for storing the audit logs' KMSKeyArn: Type: 'String' Description: 'KMS key ARN for enabling KMS encryption.' Conditions: IsMultiRegion: !Equals - !Ref MultiRegion - 'true' IsPublishToCloudWatchLogs: !Equals - !Ref PublishToCloudWatchLogs - 'true' IsUsingKmsKey: !Not [!Equals [!Ref KMSKeyArn, 'NONE']] Resources: Trail: Type: AWS::CloudTrail::Trail Properties: TrailName: !Sub ${ManagedResourcePrefix}-BaselineCloudTrail S3BucketName: !Ref AuditBucketName S3KeyPrefix: !Ref AWSLogsS3KeyPrefix IsLogging: True EnableLogFileValidation: !Ref EnableLogFileValidation KMSKeyId: !If - IsUsingKmsKey - !Ref KMSKeyArn - '' IncludeGlobalServiceEvents: !If - IsMultiRegion - True - !Ref IncludeGlobalEvents IsMultiRegionTrail: !Ref MultiRegion CloudWatchLogsLogGroupArn: !If - IsPublishToCloudWatchLogs - !GetAtt TrailLogGroup.Arn - !Ref AWS::NoValue CloudWatchLogsRoleArn: !If - IsPublishToCloudWatchLogs - !GetAtt TrailLogGroupRole.Arn - !Ref AWS::NoValue TrailLogGroup: Type: 'AWS::Logs::LogGroup' Condition: IsPublishToCloudWatchLogs Properties: LogGroupName: !Sub ${ManagedResourcePrefix}/CloudTrailLogs RetentionInDays: !Ref LogsRetentionInDays TrailLogGroupRole: Type: 'AWS::IAM::Role' Condition: IsPublishToCloudWatchLogs Properties: RoleName: !Sub ${ManagedResourcePrefix}-CloudWatchLogsRole AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Sid: CloudTrailAssumeRole Effect: Allow Principal: Service: 'cloudtrail.amazonaws.com' Action: 'sts:AssumeRole' Policies: - PolicyName: 'cloudtrail-policy' PolicyDocument: Version: '2012-10-17' Statement: - Sid: AWSCloudTrailCreateLogStream Effect: Allow Action: 'logs:CreateLogStream' Resource: !GetAtt 'TrailLogGroup.Arn' - Sid: AWSCloudTrailPutLogEvents Effect: Allow Action: 'logs:PutLogEvents' Resource: !GetAtt 'TrailLogGroup.Arn' GetAccountListLambda: Type: 'AWS::Lambda::Function' Properties: Code: ZipFile: | import logging import boto3 import json import cfnresponse from botocore.exceptions import ClientError LOGGER = logging.getLogger() LOGGER.setLevel(logging.INFO) ORG = boto3.client('organizations') def list_accounts(value='Id'): '''Return list of all AWS accounts in an organization''' result = list() output = list() try: paginator = ORG.get_paginator('list_accounts') iterator = paginator.paginate() except ClientError as exe: LOGGER.error('Unable to list instances %s', str(exe)) for page in iterator: result += page['Accounts'] for account in result: output.append(account[value]) return output def handler(event, context): '''Return list of AWS Accounts in the organization''' output = list_accounts() result = {} result['Data'] = output cfnresponse.send(event, context, cfnresponse.SUCCESS, result) Handler: index.handler Runtime: python3.7 MemorySize: 128 Role: !GetAtt GetAccountListLambdaRole.Arn Timeout: 60 GetAccountListLambdaRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - 'sts:AssumeRole' Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole - arn:aws:iam::aws:policy/AWSOrganizationsReadOnlyAccess GetAccountList: Type: 'Custom::TriggerLambda' Properties: ServiceToken: !GetAtt GetAccountListLambda.Arn