AWSTemplateFormatVersion: '2010-09-09' Description: AWS Control Tower Lifecycle Events for CloudKnox (MPCT-jqowxsqq) # ---------------------------------------------------------------------------------------------------------- # CloudFormation Template 1 of 1 - # # This templates allows newly added Control Tower accounts to be managed automatically by CloudKnox # # This template provisions infrastructure in the AWS Control Tower Management account that allows creation of Datadog # stack instances in Control Tower managed accounts whenever a new Control Tower managed account is added # # 1- Provisions KMS key to encrypt/decrypt CloudKnox secret in secrets manager # 2- Provisions Secrets Manager with CloudKnox Credentials # 3- Creates a CloudKnox Stackset in the AWS Control Tower Management Account # 4- Provisions a CloudWatchEvents Rule that is triggered based on a Control Tower Lifecycle Event # 5- Provisions a Lifecyle Lambda as a target for the CloudWatch Events Rule. # - The Lifecycle Lambda deploys a CloudKnox stack in the newly added Control Tower managed account--thus placing # that account under CloudKnox management # - Lifecycle Lambda performs a POST on the CloudKnox Account Membership API - that # registers the new AWS managed account in CloudKnox # ## ## @kmmahaj ## @mneelka - Code review updates # # ------------------------------------------------------------............................................... Parameters: serviceId: Description: REQUIRED. Default Service Account ID for CloudKnox Type: String AllowedPattern: .+ ConstraintDescription: serviceId is required accessKey: Description: REQUIRED. Default Access Key for CloudKnox Type: String AllowedPattern: .+ ConstraintDescription: accessKey is required secretKey: Description: REQUIRED. Default Secret Key for CloudKnox Type: String AllowedPattern: .+ NoEcho: true ConstraintDescription: secretKey is required apiId: Description: REQUIRED. Default API ID for CloudKnox Type: String AllowedPattern: .+ Default: "21564ec10f7943d7eb031e74e69f1abc" ConstraintDescription: api Id is required CloudKnoxSentryAccountId: Description: >- CloudKnox Sentry AWS Account ID Type: String AllowedPattern: .+ ConstraintDescription: CloudKnox sentry AWS AccountID is required url: Description: REQUIRED. Default url for CloudKnox Type: String AllowedPattern: .+ Default: "app.cloudknox.io" ConstraintDescription: url is required CloudKnoxSentryIAMRoleName: Description: IAM role provisioned for CloudKnox Sentry Type: String Default: IAM_R_KNOX_SECURITY CloudKnoxTemplateURL: Description: >- Base URL for CloudKnox CloudFormation template - CloudKnox Integration Role template Type: String Default: 'https://cloudknox-controltower-template.s3.amazonaws.com/member-account.yaml' Resources: # Secrets Management - CloudKnox Credentials CloudKnoxControlTowerKMSKey: Type: AWS::KMS::Key Properties: Description: "This is KMS Key Id used to encrypt/decrypt the Secret" EnableKeyRotation: true KeyPolicy: Version: '2012-10-17' Id: key-default-1 Statement: - Sid: Allow administration of the key Effect: Allow Principal: AWS: !Sub arn:aws:iam::${AWS::AccountId}:root Action: - kms:Create* - kms:Describe* - kms:Enable* - kms:List* - kms:Put* - kms:Update* - kms:Revoke* - kms:Disable* - kms:Get* - kms:Delete* - kms:ScheduleKeyDeletion - kms:CancelKeyDeletion Resource: '*' - Sid: Allow use of the key Effect: Allow Principal: AWS: !Sub ${AWS::AccountId} Action: - kms:Encrypt - kms:Decrypt - kms:ReEncrypt - kms:GenerateDataKey - kms:CreateGrant - kms:DescribeKey Resource: '*' Condition: StringEquals: kms:ViaService: !Sub secretsmanager.${AWS::Region}.amazonaws.com kms:CallerAccount: !Sub ${AWS::AccountId} CloudKnoxControlTowerKMSAlias: Type: AWS::KMS::Alias Properties: AliasName: !Sub "alias/CloudKnox-Control-Tower-${AWS::StackName}" TargetKeyId: Ref: CloudKnoxControlTowerKMSKey #Create Secret CloudKnoxSecretString: Type: AWS::SecretsManager::Secret Properties: Description: Credentials required for CloudKnox Name: CloudKnoxSecretString KmsKeyId: !Ref CloudKnoxControlTowerKMSKey SecretString: Fn::Join: - '' - - '{"serviceId":"' - Ref: serviceId - '","apiId": "' - Ref: apiId - '","accessKey": "' - Ref: accessKey - '","secretKey": "' - Ref: secretKey - '","url": "' - Ref: url - '","accountId": "' - Ref: AWS::AccountId - '"}' # --------------------------------------------------------------------------------------------------- # Create a CloudKnox StackSet in the Control Tower Management Account # - The CloudKnox StackSet is based on the CloudKnox member.yaml template that is provided by CloudKnox. # - The CloudKnox member.yaml template provisions the CloudKnox Cross Account Role # -------------------------------------------------------------------------------------------------- CloudKnoxStackSet: Type: AWS::CloudFormation::StackSet Properties: Description: StackSet for creating CloudKnox Integration Role StackSetName: 'CloudKnoxMemberRolev1' Parameters: - ParameterKey: CloudKnoxSentryIAMRoleName ParameterValue: !Ref CloudKnoxSentryIAMRoleName - ParameterKey: CloudKnoxSentryAccountId ParameterValue: !Ref CloudKnoxSentryAccountId PermissionModel: SELF_MANAGED AdministrationRoleARN: !Join [':', ['arn:aws:iam:', !Ref 'AWS::AccountId', 'role/service-role/AWSControlTowerStackSetRole']] ExecutionRoleName: "AWSControlTowerExecution" Capabilities: - CAPABILITY_NAMED_IAM - CAPABILITY_IAM - CAPABILITY_AUTO_EXPAND TemplateURL: !Ref CloudKnoxTemplateURL # -------------------------------------------------------------------------------------------------- # # 1- Provisions a CloudWatchEvents Rule that is triggered based on a Control Tower Lifecycle Event # 2- Provisions a Lifecyle Lambda as a target for the CloudWatch Events Rule. # # -------------------------------------------------------------------------------------------------- CloudKnoxCaptureControlTowerLifeCycleEvents: Type: AWS::Events::Rule Properties: Description: Capture Control Tower LifeCycle Events for CloudKnox and Trigger an Action EventPattern: detail: eventName: - CreateManagedAccount - UpdateManagedAccount - EnableGuardrail - DisableGuardrail - SetupLandingZone - UpdateLandingZone - RegisterOrganizationalUnit - DeregisterOrganizationalUnit eventSource: - controltower.amazonaws.com detail-type: - AWS Service Event via CloudTrail source: - aws.controltower Name: CloudKnoxCaptureControlTowerLifeCycleEvents State: ENABLED Targets: - Arn: !GetAtt "TriggerCustomizationsOnLifeCycleEvent.Arn" Id: IDCaptureControlTowerLifeCycleEvents #CloudKnox TriggerLifecyleEvent Lambda TriggerCustomizationsOnLifeCycleEvent: Type: 'AWS::Lambda::Function' Properties: FunctionName: !Join - '' - - CloudKnox_ - TriggerLifecyleEvent Role: !GetAtt CloudKnoxTriggerLifecycleEventLambdaRole.Arn Code: S3Bucket: !Sub 'cloudknox-controltower-8zwvs0yfk6at-${AWS::Region}' S3Key: !Join - '' - - CloudKnox_TriggerLifecycleEvent - / - CloudKnox_TriggerLifecycleEvent - .zip Description: CloudKnox Control Tower Lifecycle Event Lambda Handler: CloudKnox_TriggerLifecycleEvent.lambda_handler MemorySize: 256 Runtime: python3.7 Environment: Variables: CloudKnoxSentryAccountId : !Ref CloudKnoxSentryAccountId Timeout: 300 #CloudKnox Trigger LifecyleEvent Lambda Role CloudKnoxTriggerLifecycleEventLambdaRole: DependsOn: CloudKnoxSecretString Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Sid: AllowLambdaAssumeRole Effect: Allow Principal: Service: lambda.amazonaws.com Action: 'sts:AssumeRole' Policies: - PolicyName: CloudKnoxLifecycleLambdaPolicy PolicyDocument: Version: 2012-10-17 Statement: - Sid: '1' Action: - 's3:GetObject' Effect: Allow Resource: - !Sub 'arn:${AWS::Partition}:s3:::cloudknox-controltower-8zwvs0yfk6at-${AWS::Region}' - !Sub 'arn:${AWS::Partition}:s3:::cloudknox-controltower-8zwvs0yfk6at-${AWS::Region}/*' - Sid: '2' Effect: Allow Action: - 'cloudformation:CreateStackInstances' Resource: !Join [':',['arn:aws:cloudformation', !Ref 'AWS::Region', !Ref 'AWS::AccountId', 'stackset/CloudKnoxMemberRolev1:*']] - Sid: '3' Action: - 'logs:CreateLogGroup' - 'logs:CreateLogStream' - 'logs:PutLogEvents' - 'logs:DescribeLogStreams' Effect: Allow Resource: !Join [':',['arn:aws:logs', !Ref 'AWS::Region', !Ref 'AWS::AccountId', 'log-group', '/aws/lambda/CloudKnox_TriggerLifecyleEvent:*']] - Sid: '4' Action: - 'secretsmanager:GetSecretValue' - 'secretsmanager:ListSecrets' Effect: Allow Resource: !Join [':',['arn:aws:secretsmanager', !Ref 'AWS::Region', !Ref 'AWS::AccountId','secret','CloudKnoxSecretString-*']] - Sid: '5' Action: - 'kms:Decrypt' Effect: Allow Resource: !GetAtt "CloudKnoxControlTowerKMSKey.Arn" ManagedPolicyArns: - !Sub 'arn:${AWS::Partition}:iam::aws:policy/ReadOnlyAccess' - !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole' PermissionForEventsToInvokeLambdachk: Type: AWS::Lambda::Permission DependsOn: CloudKnoxSecretString Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt "TriggerCustomizationsOnLifeCycleEvent.Arn" Principal: events.amazonaws.com SourceArn: !GetAtt "CloudKnoxCaptureControlTowerLifeCycleEvents.Arn"