AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Template that implements an AWS ChatOpsBot for account vending 
Globals:
  Api:
    EndpointConfiguration: REGIONAL

Parameters:
  PortfolioId:
    Type: String
  ApprovalMailbox:
    Type: String
    Description: "The email of the mailbox that receives the emails with new account approval requests"
    Default: "default-email@amazon.com"
  ChatOpsBotName:
    Type: String
    Description: "The name of the Lex bot we will integrate"
    Default: "ChatOps"
  LexBotRegion:
    Type: String
    Description: "The region where the lex bot is deployed"
    Default: "us-east-1"
  Stage:
    Type: String
    Description: "Environment Stage: Dev, Staging, Prod"
    Default: "Prod"
  ProjectName:
    Type: String
    Description: "The name of this project"
    Default: "chatops-lex"

Resources:

  ChatOpsLexFunction:
    Type: AWS::Serverless::Function
    Properties:
      Description: !Sub "ChatOps bot. Environment: ${Stage}"
      Handler: software.aws.chatops_lex_api.StreamLambdaHandler::handleRequest
      Runtime: java8
      AutoPublishAlias: live
      DeploymentPreference:
        Type: Canary10Percent5Minutes
        Alarms:
          - !Ref CanaryErrorsAlarm
      CodeUri: ./target/chatops-lex-api-1.0-SNAPSHOT-lambda-package.zip
      MemorySize: 512
      Role: !GetAtt ChatOpsFunctionRole.Arn
      Timeout: 60
      Environment:
        Variables:
          ENVIRONMENT: !Ref Stage
          DYNAMO_TABLE: !Ref AccountVendingTable
          CONFIRM_TOPIC_ARN: !Ref ChatOpsApprovalTopic
          BOT_NAME: !Ref ChatOpsBotName
          PROJECT_NAME: !Ref ProjectName  
          LEX_BOT_REGION: !Ref LexBotRegion
      Events:
        ProxyResource:
          Type: Api
          Properties:
            Path: /{proxy+}
            Method: any
            RestApiId: !Ref Api

  MyScalableTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Properties:
      MaxCapacity: 1
      MinCapacity: 1
      ResourceId: !Sub function:${ChatOpsLexFunction}:live
      RoleARN: !Sub arn:aws:iam::${AWS::AccountId}:role/aws-service-role/lambda.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_LambdaConcurrency
      ScalableDimension: lambda:function:ProvisionedConcurrency
      ServiceNamespace: lambda
    DependsOn: ChatOpsLexFunctionAliaslive

  MyTargetTrackingScalingPolicy:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    Properties:
      PolicyName: utilization
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref MyScalableTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 0.70
        PredefinedMetricSpecification:
          PredefinedMetricType: LambdaProvisionedConcurrencyUtilization
  Api:
    Type: AWS::Serverless::Api
    Properties:
      Name: !Sub ${ProjectName}-${Stage}
      StageName: !Sub ${Stage}
      MethodSettings:
        - MetricsEnabled: True
          ResourcePath: '/*'
          HttpMethod: '*'
  SSMApiInformation:
    Type: AWS::SSM::Parameter
    Properties: 
      Name: !Sub /chatops/api/${Stage}
      AllowedPattern: >-
        https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)
      Description: "This parameter holds the address of the Api"
      Type: String
      Value: !Sub "https://${Api}.execute-api.${AWS::Region}.amazonaws.com/${Stage}/account"
  ChatOpsLambdaLogGroup:
    Type: AWS::Logs::LogGroup
    Properties: 
      LogGroupName: !Sub '/aws/lambda/${ChatOpsLexFunction}'
      RetentionInDays: 30
  LambdaDeploymentErrorMetric:
    Type: AWS::Logs::MetricFilter
    Properties:
      FilterPattern: 'Exception'
      LogGroupName: !Sub '/aws/lambda/${ChatOpsLexFunction}'
      MetricTransformations:
      - MetricValue: "1"
        MetricNamespace: ChatOpsBot
        MetricName: !Sub "${ProjectName}-Errors-Version-${ChatOpsLexFunction.Version.Version}-${Stage}"
    DependsOn: ChatOpsLambdaLogGroup
  CanaryErrorsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: Lambda function canary errors
      AlarmName: !Sub "${ProjectName}-canary-error-alarm-${Stage}"
      ComparisonOperator: GreaterThanThreshold
      DatapointsToAlarm: 1
      EvaluationPeriods: 1
      TreatMissingData: notBreaching
      MetricName: !Sub "${ProjectName}-Errors-Version-${ChatOpsLexFunction.Version.Version}-${Stage}"
      Namespace: ChatOpsBot
      Period: 60
      Statistic: Sum
      Threshold: 0
  AccountVendingTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: !Sub "${ProjectName}_account_vending_${Stage}"
      AttributeDefinitions:
        - AttributeName: UserId
          AttributeType: S
      KeySchema:
        - AttributeName: UserId
          KeyType: HASH
      BillingMode: PAY_PER_REQUEST
      TimeToLiveSpecification:
        AttributeName: expiration
        Enabled: true
  ChatOpsApprovalTopicParam:
    Type: AWS::SSM::Parameter
    Properties: 
      Description: url used to create links sent in the approval e-mail.
      Name: !Sub "${ProjectName}_account_vending_topic_${Stage}"
      Type: String
      Value: !Ref ChatOpsApprovalTopic
  ChatOpsBotNameParam:
    Type: AWS::SSM::Parameter
    Properties: 
      Description: name of the lex bot that is being integrated.
      Name: !Sub "${ProjectName}_bot_name_${Stage}"
      Type: String
      Value: !Ref ChatOpsBotName
  ChatOpsCallbackURL:
    Type: AWS::SSM::Parameter
    Properties: 
      AllowedPattern: 'https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)'
      Description: url used by chatops to create links for approval activity
      Name: !Sub "${ProjectName}_approval_url_${Stage}"
      Type: String
      Value: !Sub 'https://${Api}.execute-api.${AWS::Region}.amazonaws.com/${Stage}/account/confirm'
  ChatOpsApprovalTopic:
    Type: AWS::SNS::Topic
    Properties: 
      DisplayName: chatops_account_vending_topic
      Subscription: 
        - Endpoint: !Ref ApprovalMailbox
          Protocol: email
      TopicName: !Sub "${ProjectName}_aws_account_vending_topic_${Stage}"
  ChatOpsPortfolioAssociation:
    Type: AWS::ServiceCatalog::PortfolioPrincipalAssociation
    Properties: 
      AcceptLanguage: en
      PortfolioId: !Ref PortfolioId
      PrincipalARN: !GetAtt ChatOpsFunctionRole.Arn
      PrincipalType: IAM

  ChatOpsFunctionRole:
    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/AWSServiceCatalogEndUserFullAccess
      Policies: 
        - PolicyName: ChatOpsAccessPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: ChatOpsLexFullAccess
                Effect: Allow
                Action:
                - lex:*
                Resource: 
                - !Sub 'arn:aws:lex:${LexBotRegion}:${AWS::AccountId}:bot:${ChatOpsBotName}:*'
                - !Sub 'arn:aws:lex:${LexBotRegion}:${AWS::AccountId}:bot-alias/${ChatOpsBotName}/*'
              - Sid: ChatOpsDynamo
                Effect: Allow
                Action:
                - dynamodb:*
                Resource: !GetAtt AccountVendingTable.Arn
              - Sid: ChatOpsCallbackURL
                Effect: Allow
                Action:
                - ssm:GetParameter
                Resource: !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/chatops*'
              - Sid: ChatOpsSNSApprovalTopic
                Effect: Allow
                Action:
                - sns:*
                Resource: !Ref ChatOpsApprovalTopic
              - Sid: ChatOpsSearchProductsAsAdmin
                Effect: Allow
                Action:
                - servicecatalog:SearchProductsAsAdmin
                Resource: "*"
              - Sid: ChatOpsProvisionProducts
                Effect: Allow
                Action:
                - servicecatalog:ProvisionProduct
                Resource: "*"
              - Sid: ChatOpsDescribeProductsAsAdmin
                Effect: Allow
                Action:
                - servicecatalog:DescribeProductAsAdmin
                Resource: "*"
              - Sid: ControlTowerCreateManagedAccount
                Effect: Allow
                Action:
                - sso:GetProfile
                - sso:CreateProfile
                - sso:UpdateProfile
                - sso:AssociateProfile
                - sso:CreateApplicationInstance
                - sso:GetSSOStatus
                - sso:GetTrust
                - sso:CreateTrust
                - sso:UpdateTrust
                - sso:GetApplicationInstance
                - sso:ListDirectoryAssociations
                - sso:ListPermissionSets
                - sso:GetPermissionSet
                - sso:ListProfileAssociations
                - sso-directory:ListMembersInGroup
                - sso-directory:AddMemberToGroup
                - sso-directory:SearchGroups
                - 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
                Resource: "*"
Outputs:
  ApiUrl:
    Description: URL for application
    Value: !Sub 'https://${Api}.execute-api.${AWS::Region}.amazonaws.com/${Stage}/account'
    Export:
      Name: !Sub '${ProjectName}-api-${Stage}' 
  ApiApproval:
    Description: URL of the approval api
    Value: !Sub 'https://${Api}.execute-api.${AWS::Region}.amazonaws.com/${Stage}/account/confirm'
    Export:
      Name: !Sub '${ProjectName}-approval-api-${Stage}'