--- #----------------------------------------------------------------------------------------------------------------------- # Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance # with the License. A copy of the License is located at # # http://www.apache.org/licenses/LICENSE-2.0 # # or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES # OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions # and limitations under the License. #----------------------------------------------------------------------------------------------------------------------- AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: CDF Organization Manager Service Globals: Api: OpenApiVersion: 3.0.1 Parameters: BucketName: Description: Name of bucket to aws control tower customization manifest Type: String MinLength: 1 TemplateSnippetS3UriBase: Description: | S3 uri of directory where template snippets are stored for the account. Type: String MinLength: 1 ApplicationConfigurationOverride: Description: This allows you to override any configuration application configuration. Must be a JSON formatted string. Any configurations contained in this will override the configurations found and merged from the config files. Type: String ControlPlaneBusName: Description: Event Bus to subscribe events Type: String Environment: Description: Name of environment. Used to name the created resources. Type: String MinLength: 1 KmsKeyId: Description: The KMS key ID used to encrypt DynamoDB. Type: String AuthType: Description: Authorization type to apply to the API gateway endpoints Type: String Default: None AllowedValues: - None - Private - Cognito - LambdaRequest - LambdaToken - ApiKey - IAM MinLength: 1 ApiGatewayDefinitionTemplate: Description: | Name of the API Gateway Cloudformation definition along with the authorization method to use. Use one of the provided templates to implement no auth, private, api key, lambda request, lamdba token, or Cognito auth, or modify one to meet your own authentization requirements. The template must exist within the provided TemplateSnippetS3UriBase location. Type: String MinLength: 1 CognitoUserPoolArn: Description: Cognito user pool arn. Only required if AuthType is set to 'Cognito'. Type: String Default: 'N/A' AuthorizerFunctionArn: Description: Lambda authorizer function arn. Only required if AuthType is set to 'LambdaRequest' or 'LambdaToken'. Type: String Default: 'N/A' EnableApiGatewayAccessLogs: Description: Enales API gateway Access Logging, defaults to false if not specified. Type: String Default: 'false' AllowedValues: - 'true' - 'false' MinLength: 1 Conditions: KmsKeyIdProvided: !Not [!Equals [!Ref KmsKeyId, '']] EventBusSpecified: !Not [!Equals [!Ref ControlPlaneBusName, '']] DeployWithLambdaAuth: !Or [!Equals [!Ref AuthType, 'LambdaRequest'], !Equals [!Ref AuthType, 'LambdaToken']] EnableApiGatewayAccessLogs: !Equals [!Ref EnableApiGatewayAccessLogs, 'true'] Resources: ApiGatewayApi: 'Fn::Transform': Name: 'AWS::Include' Parameters: Location: !Sub '${TemplateSnippetS3UriBase}${ApiGatewayDefinitionTemplate}' DependsOn: - RESTLambdaFunction ApiGatewayAuthorizerInvokeRole: Condition: DeployWithLambdaAuth 'Fn::Transform': Name: 'AWS::Include' Parameters: Location: !Sub '${TemplateSnippetS3UriBase}cfn-role-lambdaRequestAuthInvokerRole.yaml' DependsOn: - RESTLambdaFunction ApiGatewayAccessLogGroup: Condition: EnableApiGatewayAccessLogs Type: 'AWS::Logs::LogGroup' Properties: LogGroupName: !Sub 'cdf-organizationmanager-apigatewayaccesslogs-${Environment}' ApplicationPolicies: Type: 'AWS::IAM::ManagedPolicy' Metadata: cfn_nag: rules_to_suppress: - id: W13 reason: 'Lambda provided permissions to manage resources in iot core' Properties: Description: 'cdf-organizationmanager application policies' Path: '/' PolicyDocument: Version: '2012-10-17' Statement: - Sid: 'dynamodb' Action: - 'dynamodb:BatchGetItem' - 'dynamodb:GetItem' - 'dynamodb:Query' - 'dynamodb:Scan' - 'dynamodb:BatchWriteItem' - 'dynamodb:PutItem' - 'dynamodb:UpdateItem' - 'dynamodb:DeleteItem' Effect: Allow Resource: - !GetAtt OrganizationManagerTable.Arn - 'Fn::Join': - '/' - - !GetAtt OrganizationManagerTable.Arn - 'index/*' - Sid: 'stsassume' Action: - 'sts:AssumeRole' Effect: Allow Resource: '*' - Sid: 'iampassrole' Action: - 'iam:PassRole' Effect: Allow Resource: !Sub 'arn:aws:iam::${AWS::AccountId}:role/cdf-organizationmanager-*' - Sid: 's3bucket' Action: - 's3:ListBucket' Effect: Allow Resource: - !Sub 'arn:aws:s3:::${BucketName}' - Sid: 's3objects' Action: - 's3:Get*' - 's3:Put*' Effect: Allow Resource: - !Sub 'arn:aws:s3:::${BucketName}/*' KmsPolicy: Type: AWS::IAM::ManagedPolicy Condition: KmsKeyIdProvided Properties: Description: 'cdf-organizationmanager policy for accessing KMS' Path: '/cdf/organizationmanager/' PolicyDocument: Version: '2012-10-17' Statement: - Action: - 'kms:Decrypt' - 'kms:Encrypt' - 'kms:ReEncrypt*' - 'kms:GenerateDataKey*' - 'kms:DescribeKey' Effect: Allow Resource: !Sub 'arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/*' EventBridgeLambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: Effect: Allow Principal: Service: - lambda.amazonaws.com Action: sts:AssumeRole Path: '/' ManagedPolicyArns: - !Ref ApplicationPolicies - !If [KmsKeyIdProvided, !Ref KmsPolicy, !Ref 'AWS::NoValue'] - arn:aws:iam::aws:policy/AWSLambdaExecute - arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole DependsOn: - ApplicationPolicies RESTLambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: Effect: Allow Principal: Service: - lambda.amazonaws.com Action: sts:AssumeRole Path: '/' ManagedPolicyArns: - !Ref ApplicationPolicies - !If [KmsKeyIdProvided, !Ref KmsPolicy, !Ref 'AWS::NoValue'] - arn:aws:iam::aws:policy/AWSLambdaExecute - arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole DependsOn: - ApplicationPolicies EventBridgeLambdaFunction: Type: AWS::Serverless::Function Properties: FunctionName: !Sub 'cdf-organization-manager-eventBridge-${Environment}' CodeUri: ../bundle.zip Handler: lambda_proxy_eventbridge.handler MemorySize: 512 Role: !GetAtt EventBridgeLambdaExecutionRole.Arn Runtime: nodejs14.x Timeout: 30 Environment: Variables: APP_CONFIG: !Ref ApplicationConfigurationOverride APP_CONFIG_DIR: 'config' AWS_DYNAMODB_ORGANIZATION_TABLE: !Ref OrganizationManagerTable Tracing: Active RESTLambdaFunction: Type: AWS::Serverless::Function Properties: FunctionName: !Sub 'cdf-organizationmanager-rest-${Environment}' CodeUri: ../bundle.zip Handler: lambda_proxy.handler MemorySize: 256 Role: !GetAtt RESTLambdaExecutionRole.Arn Runtime: nodejs14.x Timeout: 30 Environment: Variables: APP_CONFIG: !Ref ApplicationConfigurationOverride APP_CONFIG_DIR: 'config' AWS_ACCOUNT_ID: !Ref AWS::AccountId CONTROL_TOWER_MANIFEST_BUCKET: !Ref BucketName AWS_DYNAMODB_ORGANIZATION_TABLE: !Ref OrganizationManagerTable Tracing: Active Events: ProxyApiRoot: Type: Api Properties: RestApiId: !Ref ApiGatewayApi Path: / Method: ANY ProxyApiGreedy: Type: Api Properties: RestApiId: !Ref ApiGatewayApi Path: /{proxy+} Method: ANY PermissionForControlTowerEventsToInvokeLambda: Type: AWS::Lambda::Permission Properties: FunctionName: !Ref EventBridgeLambdaFunction Action: 'lambda:InvokeFunction' Principal: 'events.amazonaws.com' SourceArn: !GetAtt ControlTowerMasterAccountRule.Arn PermissionForCdfEventsToInvokeLambda: Type: AWS::Lambda::Permission Properties: FunctionName: !Ref EventBridgeLambdaFunction Action: 'lambda:InvokeFunction' Principal: 'events.amazonaws.com' SourceArn: !GetAtt CdfDeploymentRule.Arn ControlTowerMasterAccountRule: Type: AWS::Events::Rule Properties: Description: 'Control Tower Step Functions Rule' EventBusName: Fn::If: - EventBusSpecified - Ref: ControlPlaneBusName - Ref: AWS::NoValue EventPattern: source: - 'aws.controltower' detail-type: - 'AWS Service Event via CloudTrail' detail: eventName: - 'CreateManagedAccount' - 'UpdateManagedAccount' State: 'ENABLED' Targets: - Arn: !GetAtt EventBridgeLambdaFunction.Arn Id: 'TargetLambda' CdfDeploymentRule: Type: AWS::Events::Rule Properties: Description: 'CDF Deployments in multiple account Rule' EventBusName: Fn::If: - EventBusSpecified - Ref: ControlPlaneBusName - Ref: AWS::NoValue EventPattern: source: - 'com.aws.cdf.customresource' detail-type: - 'CDF Deployment Events via CloudFormation Custom Resource' detail: eventName: - 'CDFStackCreated' - 'CDFStackUpdated' State: 'ENABLED' Targets: - Arn: !GetAtt EventBridgeLambdaFunction.Arn Id: 'TargetLambda' OrganizationManagerTable: Type: AWS::DynamoDB::Table Metadata: cfn_nag: rules_to_suppress: - id: W28 reason: 'safely naming this resources to be unique by environment' - id: W73 reason: 'Can be decided by the customer, to specify the billing mode' Properties: TableName: !Sub 'cdf-organizationmanager-${Environment}' PointInTimeRecoverySpecification: PointInTimeRecoveryEnabled: true SSESpecification: Fn::If: - KmsKeyIdProvided - KMSMasterKeyId: !Ref KmsKeyId SSEEnabled: true SSEType: KMS - Ref: AWS::NoValue GlobalSecondaryIndexes: - IndexName: 'sk-pk-index' KeySchema: - AttributeName: 'sk' KeyType: 'HASH' - AttributeName: 'pk' KeyType: 'RANGE' Projection: ProjectionType: ALL ProvisionedThroughput: ReadCapacityUnits: '5' WriteCapacityUnits: '5' - IndexName: 'gsi2Key-sk-index' KeySchema: - AttributeName: 'gsi2Key' KeyType: 'HASH' - AttributeName: 'sk' KeyType: 'RANGE' Projection: ProjectionType: ALL ProvisionedThroughput: ReadCapacityUnits: '5' WriteCapacityUnits: '5' AttributeDefinitions: - AttributeName: 'pk' AttributeType: 'S' - AttributeName: 'gsi2Key' AttributeType: 'S' - AttributeName: 'sk' AttributeType: 'S' KeySchema: - AttributeName: 'pk' KeyType: 'HASH' - AttributeName: 'sk' KeyType: 'RANGE' ProvisionedThroughput: ReadCapacityUnits: '5' WriteCapacityUnits: '5' Outputs: RESTLambdaArn: Description: REST API Lambda Arn Value: !Sub '${RESTLambdaFunction.Arn}' Export: Name: !Sub 'cdf-organizationmanager-${Environment}-RESTLambdaArn' RestApiFunctionName: Description: Events REST API lambda function name Value: !Ref RESTLambdaFunction Export: Name: !Sub 'cdf-organizationmanager-${Environment}-restApiFunctionName' ApiGatewayUrl: Description: Commands REST API URL Value: !Sub 'https://${ApiGatewayApi}.execute-api.${AWS::Region}.amazonaws.com/Prod' Export: Name: !Sub 'cdf-organizationmanager-${Environment}-apigatewayurl'