# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 --- AWSTemplateFormatVersion: "2010-09-09" Transform: "AWS::Serverless-2016-10-31" Description: New account provisioning automation Parameters: ExecutionRoleName: Type: String Description: Execution IAM role name Default: AWSControlTowerExecution PortfolioIds: Type: CommaDelimitedList Description: Service Catalog Portfolio IDs Default: "" PermissionSets: Type: CommaDelimitedList Description: AWS SSO Permission Set names Default: "" SigningProfileVersionArn: Type: String Description: Code Signing Profile Version ARN GitHubOrg: Type: String Description: Source code organization Default: aws-samples GitHubRepo: Type: String Description: Source code repository Default: aws-control-tower-account-setup-using-step-functions Globals: Function: Environment: Variables: POWERTOOLS_METRICS_NAMESPACE: AccountSetup EXECUTION_ROLE_NAME: !Ref ExecutionRoleName LOG_LEVEL: INFO AWS_PARTITION: !Ref "AWS::Partition" Handler: lambda_handler.handler Layers: - !Ref DependencyLayer MemorySize: 128 # megabytes Runtime: python3.10 Tags: GITHUB_ORG: !Ref GitHubOrg GITHUB_REPO: !Ref GitHubRepo Timeout: 20 # seconds Tracing: Active Resources: CodeSigningConfig: Type: "AWS::Lambda::CodeSigningConfig" Properties: Description: AccountSetup Code Signing AllowedPublishers: SigningProfileVersionArns: - !Ref SigningProfileVersionArn CodeSigningPolicies: UntrustedArtifactOnDeployment: Enforce DependencyLayer: Type: "AWS::Serverless::LayerVersion" Metadata: BuildMethod: python3.10 Properties: LicenseInfo: MIT-0 CompatibleRuntimes: - python3.10 ContentUri: dependencies Description: DO NOT DELETE - AccountSetup - Latest versions of common Python packages RetentionPolicy: Delete RegionalFunctionLogGroup: Type: "AWS::Logs::LogGroup" UpdateReplacePolicy: Delete DeletionPolicy: Delete Metadata: cfn_nag: rules_to_suppress: - id: W84 reason: "Ignoring KMS key" Properties: LogGroupName: !Sub "/aws/lambda/${RegionalFunction}" RetentionInDays: 3 RegionalFunctionRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: Effect: Allow Principal: Service: !Sub "lambda.${AWS::URLSuffix}" Action: "sts:AssumeRole" Description: !Sub "DO NOT DELETE - Used by Lambda. Created by CloudFormation ${AWS::StackId}" Tags: - Key: "aws-cloudformation:stack-name" Value: !Ref "AWS::StackName" - Key: "aws-cloudformation:stack-id" Value: !Ref "AWS::StackId" - Key: "aws-cloudformation:logical-id" Value: RegionalFunctionRole - Key: GITHUB_ORG Value: !Ref GitHubOrg - Key: GITHUG_REPO Value: !Ref GitHubRepo RegionalFunctionPolicy: Type: "AWS::IAM::Policy" Properties: PolicyName: CloudWatchLogs PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "logs:CreateLogStream" - "logs:PutLogEvents" Resource: !GetAtt RegionalFunctionLogGroup.Arn Roles: - !Ref RegionalFunctionRole RegionalFunction: Type: "AWS::Serverless::Function" Metadata: cfn_nag: rules_to_suppress: - id: W58 reason: "Ignoring CloudWatch Logs" - id: W89 reason: "Ignoring VPC" Properties: CodeSigningConfigArn: !Ref CodeSigningConfig CodeUri: src/regional Description: DO NOT DELETE - AccountSetup - Regional Configuration Environment: Variables: POWERTOOLS_SERVICE_NAME: regional Handler: account_setup.lambda_handler.handler ReservedConcurrentExecutions: 30 Role: !GetAtt RegionalFunctionRole.Arn SSOAssignmentFunctionLogGroup: Type: "AWS::Logs::LogGroup" UpdateReplacePolicy: Delete DeletionPolicy: Delete Metadata: cfn_nag: rules_to_suppress: - id: W84 reason: "Ignoring KMS key" Properties: LogGroupName: !Sub "/aws/lambda/${SSOAssignmentFunction}" RetentionInDays: 3 SSOAssignmentFunctionRole: Type: "AWS::IAM::Role" Metadata: cfn_nag: rules_to_suppress: - id: W11 reason: "Ignoring wildcard resource" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: Effect: Allow Principal: Service: !Sub "lambda.${AWS::URLSuffix}" Action: "sts:AssumeRole" Description: !Sub "DO NOT DELETE - Used by Lambda. Created by CloudFormation ${AWS::StackId}" Policies: - PolicyName: SSOAssignmentFunctionPolicy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "organizations:ListAccounts" - "identitystore:GetGroupId" - "identitystore:ListGroups" - "sso:CreateAccountAssignment" - "sso:DescribePermissionSet" - "sso:DeleteAccountAssignment" - "sso:ListInstances" - "sso:ListPermissionSets" Resource: "*" Tags: - Key: "aws-cloudformation:stack-name" Value: !Ref "AWS::StackName" - Key: "aws-cloudformation:stack-id" Value: !Ref "AWS::StackId" - Key: "aws-cloudformation:logical-id" Value: SSOAssignmentFunctionRole - Key: GITHUB_ORG Value: !Ref GitHubOrg - Key: GITHUG_REPO Value: !Ref GitHubRepo SSOAssignmentFunctionPolicy: Type: "AWS::IAM::Policy" Properties: PolicyName: CloudWatchLogs PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "logs:CreateLogStream" - "logs:PutLogEvents" Resource: !GetAtt SSOAssignmentFunctionLogGroup.Arn Roles: - !Ref SSOAssignmentFunctionRole SSOAssignmentFunction: Type: "AWS::Serverless::Function" Metadata: cfn_nag: rules_to_suppress: - id: W58 reason: "Ignoring CloudWatch Logs" - id: W89 reason: "Ignoring VPC" Properties: CodeSigningConfigArn: !Ref CodeSigningConfig CodeUri: src/sso_assignment Description: DO NOT DELETE - AccountSetup - SSO Assignment Environment: Variables: POWERTOOLS_SERVICE_NAME: sso_assignment Events: CreateGroupEvent: Type: EventBridgeRule Properties: InputPath: "$.detail" Pattern: "detail-type": - "AWS API Call via CloudTrail" detail: eventSource: - "sso-directory.amazonaws.com" eventName: - CreateGroup Handler: account_setup.lambda_handler.handler ReservedConcurrentExecutions: 1 Role: !GetAtt SSOAssignmentFunctionRole.Arn Timeout: 300 # 5 minutes ServiceCatalogPortfolioFunctionLogGroup: Type: "AWS::Logs::LogGroup" UpdateReplacePolicy: Delete DeletionPolicy: Delete Metadata: cfn_nag: rules_to_suppress: - id: W84 reason: "Ignoring KMS key" Properties: LogGroupName: !Sub "/aws/lambda/${ServiceCatalogPortfolioFunction}" RetentionInDays: 3 ServiceCatalogPortfolioFunctionRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: Effect: Allow Principal: Service: !Sub "lambda.${AWS::URLSuffix}" Action: "sts:AssumeRole" Description: !Sub "DO NOT DELETE - Used by Lambda. Created by CloudFormation ${AWS::StackId}" Tags: - Key: "aws-cloudformation:stack-name" Value: !Ref "AWS::StackName" - Key: "aws-cloudformation:stack-id" Value: !Ref "AWS::StackId" - Key: "aws-cloudformation:logical-id" Value: ServiceCatalogPortfolioFunctionRole - Key: GITHUB_ORG Value: !Ref GitHubOrg - Key: GITHUG_REPO Value: !Ref GitHubRepo ServiceCatalogPortfolioFunctionPolicy: Type: "AWS::IAM::Policy" Properties: PolicyName: CloudWatchLogs PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "logs:CreateLogStream" - "logs:PutLogEvents" Resource: !GetAtt ServiceCatalogPortfolioFunctionLogGroup.Arn Roles: - !Ref ServiceCatalogPortfolioFunctionRole ServiceCatalogPortfolioFunction: Type: "AWS::Serverless::Function" Metadata: cfn_nag: rules_to_suppress: - id: W58 reason: "Ignoring CloudWatch Logs" - id: W89 reason: "Ignoring VPC" Properties: CodeSigningConfigArn: !Ref CodeSigningConfig CodeUri: src/service_catalog_portfolio Description: DO NOT DELETE - AccountSetup - Service Catalog Portfolio Environment: Variables: POWERTOOLS_SERVICE_NAME: service_catalog_portfolio PORTFOLIO_IDS: !Join [",", !Ref PortfolioIds] PERMISSION_SET_NAMES: !Join [",", !Ref PermissionSets] Handler: account_setup.lambda_handler.handler ReservedConcurrentExecutions: 1 Role: !GetAtt ServiceCatalogPortfolioFunctionRole.Arn Timeout: 300 # 5 minutes ControlTowerAssumePolicy: Type: "AWS::IAM::Policy" Properties: PolicyName: ControlTowerAssumePolicy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: "sts:AssumeRole" Resource: !Sub "arn:${AWS::Partition}:iam::*:role/AWSControlTowerExecution" Roles: - !Ref ServiceCatalogPortfolioFunctionRole - !Ref RegionalFunctionRole - !Ref StateMachineRole StateMachine: Type: "AWS::Serverless::StateMachine" Properties: Definition: StartAt: UpdatePasswordPolicy States: UpdatePasswordPolicy: Type: Task Resource: "arn:aws:states:::aws-sdk:iam:updateAccountPasswordPolicy" Credentials: "RoleArn.$": "States.Format('arn:aws:iam::{}:role/${ExecutionRoleName}', $.account.accountId)" Parameters: MinimumPasswordLength: 14 # https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.9 RequireSymbols: true # https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.7 RequireNumbers: true # https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.8 RequireUppercaseCharacters: true # https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.5 RequireLowercaseCharacters: true # https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.6 AllowUsersToChangePassword: true MaxPasswordAge: 90 # https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.11 PasswordReusePrevention: 24 # https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-cis-controls.html#securityhub-cis-controls-1.10 HardExpiry: false ResultPath: null # discard result and keep original input Next: PublicAccessBlock PublicAccessBlock: Type: Task Resource: "arn:aws:states:::aws-sdk:s3control:putPublicAccessBlock" Credentials: "RoleArn.$": "States.Format('arn:aws:iam::{}:role/${ExecutionRoleName}', $.account.accountId)" Parameters: PublicAccessBlockConfiguration: BlockPublicAcls: true IgnorePublicAcls: true BlockPublicPolicy: true RestrictPublicBuckets: true "AccountId.$": $.account.accountId ResultPath: null # discard result and keep original input Next: Route53PolicyDocument Route53PolicyDocument: Type: Pass Result: PolicyDocument: |- \{ "Version": "2012-10-17", "Statement": [ \{ "Sid": "Route53LogsToCloudWatchLogs", "Effect": "Allow", "Principal": \{ "Service": "route53.amazonaws.com" \}, "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:us-east-1:{}:log-group:/aws/route53/*", "Condition": \{ "StringEquals": \{ "aws:SourceAccount": "{}" \} \} \} ] \} ResultPath: "$.Policy" Next: Route53LoggingPolicy Route53LoggingPolicy: Type: Task Resource: "arn:aws:states:::aws-sdk:cloudwatchlogs:putResourcePolicy" Credentials: "RoleArn.$": "States.Format('arn:aws:iam::{}:role/${ExecutionRoleName}', $.account.accountId)" Parameters: PolicyName: AWSServiceRoleForRoute53 "PolicyDocument.$": States.Format($.Policy.PolicyDocument, $.account.accountId, $.account.accountId) ResultPath: null # discard result and keep original input Next: DescribeRegions DescribeRegions: Type: Task Resource: "arn:aws:states:::aws-sdk:ec2:describeRegions" Parameters: Filters: - Name: opt-in-status Values: - opt-in-not-required AllRegions: false ResultSelector: "RegionNames.$": "$.Regions[*].RegionName" ResultPath: "$.Regions" Next: AllRegions AllRegions: Type: Map ItemsPath: "$.Regions.RegionNames" MaxConcurrency: 0 Parameters: "account_id.$": "$.account.accountId" "region.$": "$$.Map.Item.Value" Iterator: StartAt: EbsEncryptionByDefault States: EbsEncryptionByDefault: Type: Task Resource: "arn:aws:states:::aws-sdk:ec2:enableEbsEncryptionByDefault" Credentials: "RoleArn.$": "States.Format('arn:aws:iam::{}:role/${ExecutionRoleName}', $.account_id)" Parameters: {} ResultPath: null # discard result and keep original input Next: DisableSsmPublicSharing DisableSsmPublicSharing: Type: Task Resource: "arn:aws:states:::aws-sdk:ssm:updateServiceSetting" Credentials: "RoleArn.$": "States.Format('arn:aws:iam::{}:role/${ExecutionRoleName}', $.account_id)" Parameters: "SettingId.$": "States.Format('arn:aws:ssm:{}:{}:servicesetting/ssm/documents/console/public-sharing-permission', $.region, $.account_id)" SettingValue: Disable Catch: - ErrorEquals: - States.ALL ResultPath: null # discard result and keep original input Next: Regional ResultPath: null # discard result and keep original input Next: Regional Regional: Type: Task Resource: !GetAtt RegionalFunction.Arn Retry: - ErrorEquals: - Lambda.TooManyRequestsException - Lambda.ServiceException - Lambda.AWSLambdaException - Lambda.SdkClientException IntervalSeconds: 2 MaxAttempts: 6 BackoffRate: 2 TimeoutSeconds: 20 End: true ResultPath: null # discard result and keep original input Next: SSOAssignment SSOAssignment: Type: Task Resource: !GetAtt SSOAssignmentFunction.Arn Retry: - ErrorEquals: - Lambda.TooManyRequestsException - Lambda.ServiceException - Lambda.AWSLambdaException - Lambda.SdkClientException IntervalSeconds: 2 MaxAttempts: 6 BackoffRate: 2 TimeoutSeconds: 300 ResultPath: null # discard result and keep original input Next: ServiceCatalogPortfolio ServiceCatalogPortfolio: Type: Task Resource: !GetAtt ServiceCatalogPortfolioFunction.Arn Retry: - ErrorEquals: - Lambda.TooManyRequestsException - Lambda.ServiceException - Lambda.AWSLambdaException - Lambda.SdkClientException IntervalSeconds: 2 MaxAttempts: 6 BackoffRate: 2 TimeoutSeconds: 300 End: true DefinitionSubstitutions: ExecutionRoleName: !Ref ExecutionRoleName Events: CreateAccountEvent: Type: EventBridgeRule Properties: InputPath: "$.detail.serviceEventDetails.createManagedAccountStatus" Pattern: source: - "aws.controltower" "detail-type": - "AWS Service Event via CloudTrail" detail: eventName: - CreateManagedAccount serviceEventDetails: createManagedAccountStatus: state: - SUCCEEDED Policies: - Version: "2012-10-17" Statement: - Effect: Allow Action: "ec2:DescribeRegions" Resource: "*" - Effect: Allow Action: "lambda:InvokeFunction" Resource: - !GetAtt SSOAssignmentFunction.Arn - !GetAtt ServiceCatalogPortfolioFunction.Arn - !GetAtt RegionalFunction.Arn Tags: GITHUB_ORG: !Ref GitHubOrg GITHUB_REPO: !Ref GitHubRepo Tracing: Enabled: true Type: STANDARD