AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFormation Template to create Lambda function which is used for IntelliFend Integration.

Metadata: 
  AWS::CloudFormation::Interface: 
    ParameterGroups: 
      - 
        Label: 
          default: "ARN of secrets manager for MarketplacePutDeploymentParameters"
        Parameters: 
          - MarketplacePutDeploymentParameters
    ParameterLabels: 
      MarketplacePutDeploymentParameters: 
        default: Enter the ARN of the secrets manager you previously created consits of IntellifendAccountKey and other account config
Parameters:
  MarketplacePutDeploymentParameters:
    Type: String
    Description: Leave it empty if dry-run

Conditions:
  IsDryRun: !Equals
    - !Ref MarketplacePutDeploymentParameters
    - ''
Resources:
  IntelliFendDryRunSecret:
    Type: AWS::SecretsManager::Secret
    Condition: IsDryRun
    Properties:
      Description: IntelliFend Dry-run Secret
      Name: !Join ['-', ['IntelliFendDryRunSecret', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
      SecretString: |
        {
            "IntellifendAccountKey": "54725881-9025-11ef-a170-0ab71543befd",
            "IntellifendApiEndpoint": "https://stg.techtank9.com/backoffice-backend/aws/cloudformation/callback",
            "IntellifendWorkerCodeS3Bucket": "intellifend",
            "IntellifendWorkerCodeS3Key": "stg/cloudfront-integration/v1/worker.zip",
            "IntellifendWorkerMgmtCodeS3Key": "stg/cloudfront-integration/v1/worker-mgmt.zip"
        }
  IntelliFendWorkerSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Description: IntelliFend Worker Secret
      Name: !Join ['-', ['IntelliFendWorkerSecret', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
      SecretString: !Sub |
        {
          "param1": "sample_value1",
          "param2": "sample_value2"
        }

  IntelliFendManagementSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name: !Join ['-', ['IntelliFendManagementSecret', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
      Description: IntelliFend Management Secret
      GenerateSecretString:
        IncludeSpace: false
        ExcludePunctuation: true
        PasswordLength: 30
        GenerateStringKey: token
        SecretStringTemplate: !Sub '{}'

  IntelliFendWorkerExecutionRole:
    Type: AWS::IAM::Role
    DependsOn:
      - IntelliFendWorkerSecret
    Metadata:
      SamResourceId: IntelliFendWorkerExecutionRole
    Properties:
      Description: IntelliFend Worker Execution Role
      RoleName: !Join ['-', ['IntelliFendWorkerExecutionRole', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
                - edgelambda.amazonaws.com
      Policies:
      - PolicyName: LambdaExecutionPolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:PutLogEvents
              Resource: arn:aws:logs:*:*:*
      - PolicyName: AWSSecretAccess
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - secretsmanager:GetSecretValue
              Resource: !Ref IntelliFendWorkerSecret
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  IntelliFendWorker:
    Type: AWS::Lambda::Function
    DependsOn:
      - IntelliFendWorkerExecutionRole
    Properties:
      Description: IntelliFend Worker
      FunctionName: !Join ['-', ['IntelliFendWorker', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
      Handler: index.handler
      Runtime: nodejs20.x
      PackageType: Zip
      Code: 
        S3Bucket: !Sub
          - "{{resolve:secretsmanager:${secret}:SecretString:IntellifendWorkerCodeS3Bucket}}"
          - secret: !If [IsDryRun, !Ref IntelliFendDryRunSecret, !Ref MarketplacePutDeploymentParameters]
        S3Key: !Sub
          - "{{resolve:secretsmanager:${secret}:SecretString:IntellifendWorkerCodeS3Key}}"
          - secret: !If [IsDryRun, !Ref IntelliFendDryRunSecret, !Ref MarketplacePutDeploymentParameters]
      MemorySize: 128
      Timeout: 3
      Role: !GetAtt IntelliFendWorkerExecutionRole.Arn
  IntelliFendManagementExecutionRole:
    Type: AWS::IAM::Role
    DependsOn:
      - IntelliFendManagementSecret
    Metadata:
      SamResourceId: IntelliFendManagementExecutionRole
    Properties:
      Description: IntelliFend Management Execution Role
      RoleName: !Join ['-', ['IntelliFendManagementExecutionRole', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Action: sts:AssumeRole
          Principal:
            Service:
              - lambda.amazonaws.com
      Policies:
      - PolicyName: LogsPolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
              - logs:CreateLogGroup
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource: arn:aws:logs:*:*:*
      - PolicyName: AWSSecretAccess
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
              - secretsmanager:GetSecretValue
              - secretsmanager:PutSecretValue
            Resource: 
              - !Ref IntelliFendWorkerSecret
              - !Ref IntelliFendManagementSecret
      - PolicyName: IntellifendLambdaUpdate
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
              - lambda:ListVersionsByFunction
              - lambda:GetFunction
              - lambda:GetFunctionConfiguration
              - lambda:EnableReplication
              - lambda:UpdateFunctionCode
              - lambda:PublishVersion
              - s3:GetObject
            Resource:
              - !Join
                  - ''
                  - - !Sub 'arn:aws:lambda:*:${AWS::AccountId}:function:'
                    - 'IntelliFendWorker-'
                    - !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
                    - ':*'
              - !Join
                  - ''
                  - - !Sub 'arn:aws:lambda:*:${AWS::AccountId}:function:'
                    - 'IntelliFendManagement-'
                    - !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
                    - ':*'
              - !Join
                  - ''
                  - - !Sub 'arn:aws:lambda:*:${AWS::AccountId}:function:'
                    - 'IntelliFendWorker-'
                    - !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
              - !Join ['', ['arn:aws:s3:::', !Sub ["{{resolve:secretsmanager:${secret}:SecretString:IntellifendWorkerCodeS3Bucket}}", { secret: !If [IsDryRun, !Ref IntelliFendDryRunSecret, !Ref MarketplacePutDeploymentParameters] }], '/*']]
      - PolicyName: CloudFrontUpdate
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
              - cloudfront:GetDistribution
              - cloudfront:UpdateDistribution
              - cloudfront:GetDistributionConfig
              - cloudfront:CreateInvalidation
            Resource: !Join ['', [!Sub 'arn:aws:cloudfront::${AWS::AccountId}:distribution/', '*']]
          - Effect: Allow
            Action:
              - cloudfront:ListDistributions
            Resource: "*"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
  IntelliFendManagement:
    Type: AWS::Lambda::Function
    DependsOn:
      - IntelliFendManagementExecutionRole
      - IntelliFendWorker
      - IntelliFendManagementSecret
    Properties:
      Description: IntelliFend Management
      FunctionName: !Join ['-', ['IntelliFendManagement', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
      Handler: index.handler
      Runtime: nodejs20.x
      Code:
        S3Bucket: !Sub
          - "{{resolve:secretsmanager:${secret}:SecretString:IntellifendWorkerCodeS3Bucket}}"
          - secret: !If [IsDryRun, !Ref IntelliFendDryRunSecret, !Ref MarketplacePutDeploymentParameters]
        S3Key: !Sub
          - "{{resolve:secretsmanager:${secret}:SecretString:IntellifendWorkerMgmtCodeS3Key}}"
          - secret: !If [IsDryRun, !Ref IntelliFendDryRunSecret, !Ref MarketplacePutDeploymentParameters]
      MemorySize: 128
      Timeout: 10
      Role: !GetAtt IntelliFendManagementExecutionRole.Arn
      Environment:
        Variables:
          IntellifendLambdaFunctionArn: !GetAtt IntelliFendWorker.Arn
          IntellifendLambdaFunctionName: !Join ['-', ['IntelliFendWorker', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
          IntellifendWorkerFunctionBucket: !Sub
            - "{{resolve:secretsmanager:${secret}:SecretString:IntellifendWorkerCodeS3Bucket}}"
            - secret: !If [IsDryRun, !Ref IntelliFendDryRunSecret, !Ref MarketplacePutDeploymentParameters]
          IntellifendWorkerFunctionKey: !Sub
            - "{{resolve:secretsmanager:${secret}:SecretString:IntellifendWorkerCodeS3Key}}"
            - secret: !If [IsDryRun, !Ref IntelliFendDryRunSecret, !Ref MarketplacePutDeploymentParameters]
          IntellifendWorkerMgmtFunctionKey: !Sub
            - "{{resolve:secretsmanager:${secret}:SecretString:IntellifendWorkerMgmtCodeS3Key}}"
            - secret: !If [IsDryRun, !Ref IntelliFendDryRunSecret, !Ref MarketplacePutDeploymentParameters]
          IntellifendManagementSecretName: !Ref IntelliFendManagementSecret
          WorkerSecretName: !Ref IntelliFendWorkerSecret

  IntelliFendManagementURL:
    Type: AWS::Lambda::Url
    DependsOn:
      - IntelliFendManagement
    Properties:
      AuthType: NONE
      TargetFunctionArn: !GetAtt IntelliFendManagement.Arn

  IntelliFendManagementInvokePermission:
    Type: AWS::Lambda::Permission
    DependsOn:
      - IntelliFendManagement
    Properties:
      Action: lambda:InvokeFunctionUrl
      FunctionName: !Ref IntelliFendManagement
      FunctionUrlAuthType: NONE
      Principal: '*'

  IntelliFendPostCompletionNotifier:
    Type: AWS::Lambda::Function
    DependsOn:
      - IntelliFendManagementURL
      - IntelliFendManagementSecret
    Properties:
      Description: IntelliFend Post Completion Notifier
      FunctionName: !Join ['-', ['IntelliFendPostCompletionNotifier', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
      Handler: index.send
      Runtime: nodejs20.x
      Timeout: 10
      Role: !GetAtt IntelliFendWorkerExecutionRole.Arn
      Code:
        ZipFile: !Sub
          - |
            const response = require('cfn-response');
            const https = require("https");
            const url = require("url");

            exports.SUCCESS = "SUCCESS";
            exports.FAILED = "FAILED";

            exports.send = function(event, context, responseStatus, responseData, physicalResourceId, noEcho) {
                return new Promise((resolve, reject) => {
                    const intellifendApiUrl = url.parse('${IntellifendApiEndpoint}');
                    const options = {
                        hostname: intellifendApiUrl.hostname,
                        port: 443,
                        path: intellifendApiUrl.path,
                        method: "POST",
                        headers: {
                          "Content-Type": "application/json",
                          "Intellifend-Account-Key": '${IntellifendAccountKey}'
                        }
                    };

                    const requestBody = JSON.stringify({
                      'url': '${IntelliFendManagementURL}',
                      'token': '${IntelliFendManagementToken}'
                    });

                    const request = https.request(options, function(response) {
                        console.log("Status code: " + parseInt(response.statusCode));
                        resolve(context.done());
                    });

                    request.on("error", function(error) {
                        console.log("send(..) failed executing https.request(..): " + error);
                        reject(context.done(error));
                    });

                    request.write(requestBody);
                    request.end();
                    response.send(event, context, response.SUCCESS, {});
                    console.log("Callback request has been sent successfully.")
                })
            }
          - IntellifendApiEndpoint: !Sub
              - "{{resolve:secretsmanager:${secret}:SecretString:IntellifendApiEndpoint}}"
              - secret: !If [IsDryRun, !Ref IntelliFendDryRunSecret, !Ref MarketplacePutDeploymentParameters]
            IntellifendAccountKey: !Sub
              - "{{resolve:secretsmanager:${secret}:SecretString:IntellifendAccountKey}}"
              - secret: !If [IsDryRun, !Ref IntelliFendDryRunSecret, !Ref MarketplacePutDeploymentParameters]
            IntelliFendManagementToken: !Join ['', ['{{resolve:secretsmanager:IntelliFendManagementSecret-', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]] ,':SecretString:token}}']]
            IntelliFendManagementURL: !GetAtt IntelliFendManagementURL.FunctionUrl

  InvokeLambdaFunction:
    Type: Custom::InvokeLambdaFunction
    Properties:
      ServiceToken: !GetAtt IntelliFendPostCompletionNotifier.Arn
Outputs:
  WorkerFunctionName:
    Description: IntelliFend worker function name
    Value:
      Ref: IntelliFendWorker
    Export:
      Name: !Join ['-', ['IntelliFendWorker', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
  ManagementFunctionName:
    Description: IntelliFend management function name
    Value:
      Ref: IntelliFendManagement
    Export:
      Name: !Join ['-', ['IntelliFendManagement', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]
  ManagementFunctionUrl:
    Description: IntelliFend management function url
    Value:
      Fn::GetAtt: IntelliFendManagementURL.FunctionUrl
    Export:
      Name: !Join ['-', ['IntelliFendManagementUrl', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]