AWSTemplateFormatVersion: '2010-09-09'
Description: Control Tower Batch AWS Account Creation And SSO Assignment Automation
Parameters:
  pAwsSsoInstanceArn: 
    Type: String
    Default: 'arn:aws:sso:::instance/ssoins-abcd12345'
    Description: 'Specify your AWS SSO Instance ARN.'
  pIdentityStoreID:
    Type: String
    Default: 'd-123456789'
    Description: 'Specify your AWS SSO Identity Store ID'
  pUserGroupID:
    Type: String
    Default: '123456-123456-12345-12345'
    Description: 'Specify the User Group ID that you would like to assign the above Permission Set'
  pAdminSNSEmail:
    Type: String
    Default: 'john.doe@company.com'
    Description: 'Specify the Admin Email Address for AWS Accounts creation notifications'
  pBucket:
    Type: String
    Default: 'ct-acct-sso-automation'
    Description: 'Bucket name where the bulk account files will be uploaded'
  pPermissionSet:
    Type: String
    Default: 'arn:aws:sso:::permissionSet/ssoins-abc1234567/ps-1234567abc'
    Description: 'Specify the ARN of the created AWS SSO Permission Set to be assigned to the User Group.'

Resources:
  S3ProvisionAccounts:
    Type: AWS::S3::Bucket
    DependsOn: LambdaInvokePermission
    Properties:
      BucketName: !Ref pBucket
      NotificationConfiguration:
        LambdaConfigurations:
          - Event: 's3:ObjectCreated:*'
            Function: !GetAtt NewAccountHandlerLambda.Arn
            
  NewAccountDetailsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      KeySchema:
        - AttributeName: AccountName
          KeyType: HASH
      AttributeDefinitions:
        - AttributeName: AccountName
          AttributeType: S
      ProvisionedThroughput:
        ReadCapacityUnits: 10
        WriteCapacityUnits: 10
      StreamSpecification:
        StreamViewType: NEW_AND_OLD_IMAGES
    UpdateReplacePolicy: Delete
    DeletionPolicy: Delete
  
  NewAccountHandlerLambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
        Version: "2012-10-17"
      ManagedPolicyArns:
        - !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
      Policies:
        - PolicyName: ct-s3-inline-policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 's3:GetObject'
                  - 's3:GetBucketNotification'
                  - 's3:PutBucketNotification'
                Resource:
                  - !Sub 'arn:${AWS::Partition}:s3:::${pBucket}'
                  - !Sub 'arn:${AWS::Partition}:s3:::${pBucket}/*'
                  
  NewAccountHandlerPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Statement:
          - Action:
              - dynamodb:Query
              - dynamodb:GetItem
              - dynamodb:PutItem
              - dynamodb:UpdateItem
              - dynamodb:DeleteItem
              - dynamodb:scan
            Effect: Allow
            Resource:
              - Fn::GetAtt:
                  - NewAccountDetailsTable
                  - Arn
              - Ref: AWS::NoValue
          - Action:
              - organizations:ListAccountsForParent
              - organizations:ListRoots
              - organizations:ListAccountsForParent
              - organizations:ListOrganizationalUnitsForParent
            Effect: Allow
            Resource: '*'
        Version: "2012-10-17"
      PolicyName: NewAccountHandlerPolicy
      Roles:
        - Ref: NewAccountHandlerLambdaExecutionRole
        
  NewAccountHandlerLambda:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: awsiammedia
        S3Key: public/sample/952-Automate-AWS-Accounts-Creation-SSO-Users-Assignment/batchcreation_lambda.zip
      Handler: new_account_handler.account_handler
      Role:
        !GetAtt NewAccountHandlerLambdaExecutionRole.Arn
      Runtime: python3.7
      Environment:
        Variables:
          TABLE_NAME:
            Ref: NewAccountDetailsTable
      Timeout: 300
    DependsOn:
      - NewAccountHandlerPolicy

  LambdaInvokePermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      FunctionName: !GetAtt NewAccountHandlerLambda.Arn
      Action: 'lambda:InvokeFunction'
      Principal: s3.amazonaws.com
      SourceAccount: !Ref 'AWS::AccountId'
      SourceArn: !Sub 'arn:aws:s3:::${pBucket}'

  CreateManagedAccountLambda:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: awsiammedia
        S3Key: public/sample/952-Automate-AWS-Accounts-Creation-SSO-Users-Assignment/account_create_lambda.zip
      Handler: account_create.lambda_handler
      MemorySize: 128
      Role: !GetAtt "CreateManagedAccountLambdaRole.Arn"
      Runtime: python3.7
      Timeout: 300
      Environment:
        Variables:
          TABLE_NAME:
            Ref: NewAccountDetailsTable
          PRINCIPAL_ARN:
            !GetAtt "CreateManagedAccountLambdaRole.Arn"

  PermissionForEventsToInvokeLambdachk:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !GetAtt "CreateManagedAccountLambda.Arn"
      Principal: events.amazonaws.com
      SourceArn: !GetAtt "CaptureControlTowerLifeCycleEvents.Arn"

  CreateManagedAccountLambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
            Condition: {}
      Path: /
      Policies:
        - PolicyName: ct-admin-inline-policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'sso:GetProfile'
                  - 'sso:CreateProfile'
                  - 'sso:UpdateProfile'
                  - 'sso:AssociateProfile'
                  - 'sso:CreateApplicationInstance'
                  - 'sso:GetSSOStatus'
                  - 'sso:GetTrust'
                  - 'sso:CreateTrust'
                  - 'sso:UpdateTrust'
                  - 'sso:GetPeregrineStatus'
                  - 'sso:GetApplicationInstance'
                  - 'sso:ListDirectoryAssociations'
                  - 'sso:ListPermissionSets'
                  - 'sso:GetPermissionSet'
                  - 'sso:ProvisionApplicationInstanceForAWSAccount'
                  - 'sso:ProvisionApplicationProfileForAWSAccountInstance'
                  - 'sso:ProvisionSAMLProvider'
                  - 'sso:ListProfileAssociations'
                  - 'sso-directory:ListMembersInGroup'
                  - 'sso-directory:SearchGroups'
                  - 'sso-directory:SearchGroupsWithGroupName'
                  - 'sso-directory:SearchUsers'
                  - 'sso-directory:CreateUser'
                  - 'sso-directory:DescribeGroups'
                  - 'sso-directory:DescribeDirectory'
                  - 'sso-directory:GetUserPoolInfo'
                  - 'controltower:CreateManagedAccount'
                  - 'controltower:DescribeManagedAccount'
                  - 'controltower:DeregisterManagedAccount'
                  - 's3:GetObject'
                  - 'organizations:describeOrganization'
                  - 'sso:DescribeRegisteredRegions'
                  - 'servicecatalog:DisassociatePrincipalFromPortfolio'
                  - 'servicecatalog:AssociatePrincipalWithPortfolio'
                Resource:  '*'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess
        - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
        - arn:aws:iam::aws:policy/AWSOrganizationsFullAccess
        - arn:aws:iam::aws:policy/CloudWatchEventsFullAccess
        - arn:aws:iam::aws:policy/AWSServiceCatalogAdminReadOnlyAccess
        - arn:aws:iam::aws:policy/AWSServiceCatalogEndUserFullAccess

  CreateAccountAssignmentLambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
            Condition: {}
      Path: /
      Policies:
        - PolicyName: create-account-assignment-inline-policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'sso-directory:ListGroupsForUser'
                  - 'sso:ProvisionPermissionSet'
                  - 'sso:ListPermissionSets'
                  - 'sso:CreateAccountAssignment'
                  - 'sso:ListPermissionSetsProvisionedToAccount'
                  - 'sso:DescribeAccountAssignmentCreationStatus'
                  - 'sso-directory:ListMembersInGroup'
                  - 'sso:SearchUsers'
                  - 'sso:GetPermissionSet'
                  - 'sso:CreatePermissionSet'
                  - 'sso-directory:DescribeGroups'
                  - 'sso-directory:DescribeUsers'
                  - 'sso-directory:SearchGroups'
                  - 'sso:ListAccountAssignments'
                  - 'sso:ListAccountsForProvisionedPermissionSet'
                  - 'sso-directory:DescribeDirectory'
                  - 'sso:SearchGroups'
                  - 'sso-directory:SearchUsers'
                  - 'sso:ListInstances'
                  - 'sso:DescribePermissionSetProvisioningStatus'
                  - 'sso:DescribePermissionSet'
                  - 'identitystore:DescribeUser'
                  - 'identitystore:ListGroups'
                  - 'identitystore:ListUsers'
                  - 'identitystore:DescribeGroup'
                Resource:  '*'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
        - arn:aws:iam::aws:policy/AWSOrganizationsFullAccess
        - arn:aws:iam::aws:policy/CloudWatchEventsFullAccess
        - arn:aws:iam::aws:policy/AWSServiceCatalogAdminReadOnlyAccess
        - arn:aws:iam::aws:policy/AWSServiceCatalogEndUserFullAccess             

  CaptureControlTowerLifeCycleEvents:
    Type: AWS::Events::Rule
    Properties:
      Description: Capture Control Tower LifeCycle Events and Trigger an Action
      EventPattern:
        detail:
          eventName:
          - CreateManagedAccount
          eventSource:
          - controltower.amazonaws.com
        detail-type:
        - AWS Service Event via CloudTrail
        source:
        - aws.controltower
      State: ENABLED
      Targets:
      - Arn: !GetAtt "CreateManagedAccountLambda.Arn"
        Id: CreateManagedAccountLambdaEvents
      - Arn: !GetAtt "CreateAccountAssignmentLambda.Arn"
        Id: CreateAccountAssignmentLambdaEvents
      - Arn: !Ref "NewAccountSNSTopic"
        Id: NewAccountSNSTopic

  PermissionForEventstoInvokeCreateAccountAssignmentLambda:
      Type: AWS::Lambda::Permission
      Properties:
        Action: lambda:InvokeFunction
        FunctionName: !GetAtt "CreateAccountAssignmentLambda.Arn"
        Principal: events.amazonaws.com
        SourceArn: !GetAtt "CaptureControlTowerLifeCycleEvents.Arn"

  CreateAccountAssignmentLambda:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: awsiammedia
        S3Key: public/sample/952-Automate-AWS-Accounts-Creation-SSO-Users-Assignment/create_account_assignment_lambda.zip
      Handler: create_account_assignment_handler.handler
      Role: !GetAtt "CreateAccountAssignmentLambdaRole.Arn"
      Runtime: python3.7
      Environment:
        Variables:
          SSO_INSTANCE_ARN: !Ref pAwsSsoInstanceArn
          SSO_PERMISSION_SET_ARN: !Ref pPermissionSet
          IDENTITY_STORE_ID: !Ref pIdentityStoreID
          USER_GROUP_ID: !Ref pUserGroupID
      Timeout: 300

  NewAccountSNSTopic:
    Type: AWS::SNS::Topic
    Properties:
      Subscription:
        - Endpoint: !Ref pAdminSNSEmail
          Protocol: email 
      DisplayName: 'AWSAccountCreationNotification'    
      TopicName: 'AWSAccountCreationNotification'

  NewAccountSNSTopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties:
      PolicyDocument:
        Id: MyTopicPolicy
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service: events.amazonaws.com
          Action: sns:Publish
          Resource: "*"
      Topics:
      - !Ref NewAccountSNSTopic

  EventSourceMapping:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      EventSourceArn: !GetAtt
        - NewAccountDetailsTable
        - StreamArn
      FunctionName: !GetAtt
        - CreateManagedAccountLambda
        - Arn
      StartingPosition: LATEST