---
AWSTemplateFormatVersion: 2010-09-09
Description: 'Master manifest for Machine Learning usecase'
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: General configuration
        Parameters:
          - ADPS3BucketName
          - ADPS3BucketRegion
          - BasePlatformVersion
          - UseCaseVersion
          - DnsZoneType
          - EnableExternalAccess
      - Label:
          default: Application configuration
        Parameters:
          - UseKubernetesModel
          - UseSagemakerModel
          - DeployUsingLambda
          - DeployUsingCodeBuild
          - MlflowSetExperiment
          - ChurnEndpointName
          - DockerCPImage
          - DockerMlModelImage
          - DockerRegistry
      - Label:
          default: Notebook-Instance configuration
        Parameters:
          - DownloadFile
          - S3BucketName
          - NotebookInstanceName
          - NotebookInstanceType
          - GitHubRepository
      - Label:
          default: Deploy configuration
        Parameters:
          - MlflowServiceImageRepository
          - BranchName
          - RepositoryID
          - GitConnectionName

Parameters:
  DownloadFile:
    Type: String
    Description: 'Enter the path to the file from s3 for download to notebook'
    Default: 'etc/example.ipynb'
  S3BucketName:
    Type: String
    Description: 'Enter your name of S3 bucket'
    Default: 'bucket'

  ADPS3BucketName:
    Type: String
    AllowedPattern: ^[0-9a-z]+([0-9a-z-]*[0-9a-z])*$
    ConstraintDescription:
      'Bucket name can include numbers, lowercase letters, and hyphens (-).
       It cannot start or end with a hyphen (-).'
    Description:
      'ADP bucket name with cloudformation scripts can include numbers,
      lowercase letters, and hyphens (-). It cannot start or end with a hyphen (-).'
    Default: 'griddynamics-analytical-data-platform-releases'
  UseCaseVersion:
    Type: String
    Description: 'UseCase build version.'
    Default: '2023-02-16-marketplace'
  ADPS3BucketRegion:
    Type: String
    Description: 'The AWS Region where the ADP S3 bucket (ADPS3BucketName) is hosted.'
    Default: 'us-east-1'
  BasePlatformVersion:
    Type: String
    Description: 'BasePlatform build version.'
    Default: '2023-02-16-marketplace'

  NotebookInstanceName:
    Type: String
    Description: 'Name of the Amazon SageMaker Notebook instance'
    Default: 'adp-notebook-instance'
  NotebookInstanceType:
    Description: 'The EC2 instance type for the data lake Amazon SageMaker Notebook instance.'
    ConstraintDescription: 'Must select a valid Notebook instance type'
    Type: String
    AllowedValues:
      - ml.t2.medium
      - ml.t2.large
      - ml.t2.xlarge
      - ml.t3.medium
      - ml.t3.large
    Default: 'ml.t3.large'
  GitHubRepository:
    Description: '[OPTIONAL] The Git repository associated with the notebook instance as its default code repository'
    Type: String
    Default: ""
  MlflowSetExperiment:
    Description: 'Name of the Experiment into mlflow'
    Type: String
    Default: 'adp-mlops-experiment'

  DockerCPImage:
    Type: String
    Description: 'Docker image name for ChurnPrediction application.'
    Default: 'griddynamics/adp-churn-predictions'
  DockerRegistry:
    Type: String
    Description: 'Docker registry.'
    Default: '125667932402.dkr.ecr.us-east-1.amazonaws.com'
  EnableExternalAccess:
    Type: String
    Description:
      'Allow or deny access to UI from outside of VPC (disables ELB for EKS-based applications).
      Disabled by default to minimize unauthorized access.'
    AllowedValues: [ "Enabled", "Disabled" ]
    Default: 'Disabled'
  DnsZoneType:
    Type: String
    Description:
      'Should match with ADP Base Platform settings. Choose public if you already delegated responsibility for the subdomain to Route 53. This subdomain
      should be configured in Route53HostedZoneName. Otherwise choose a private DNS zone.'
    AllowedValues: [ "public", "private" ]
    Default: 'private'
  ChurnEndpointName:
    Type: String
    Description: 'Name of Endpoint into SageMaker Inference Endpoints'
    Default: 'xgboost-churn-predictions'

  MlflowServiceImageRepository:
    Type: String
    Description: Enter your path of image. Repository should be created before starting this case
    Default: 'griddynamics/adp-mlflow-churn-predictions'
  BranchName:
    Type: String
    Description: The name of the branch for the repository where the source change was made
    Default: "main"
  RepositoryID:
    Type: String
    Description: The owner and name of the repository where source changes are to be detected. For example, griddynamics/test
    Default: ""
  GitConnectionName:
    Type: String
    Description: The name of the connection. Connection names must be unique in an AWS user account. MaxLength of name - 32.
    Default: "MlflowGHConnection"
    MaxLength: '32'
  DockerMlModelImage:
    Type: String
    Default: 'griddynamics/adp-model-deployment'
  UseKubernetesModel:
    Type: String
    AllowedValues: [ "Enabled", "Disabled" ]
    Default: 'Disabled'
  UseSagemakerModel:
    Type: String
    AllowedValues: [ "Enabled", "Disabled" ]
    Default: 'Enabled'
  DeployUsingCodeBuild:
    Type: String
    Description: '[WARNING] Should match with DeployUsingLambda parameter. If parameter DeployUsingLambda was enabled, then this parameter must be disabled!'
    AllowedValues: [ "Enabled", "Disabled" ]
    Default: 'Disabled'
  DeployUsingLambda:
    Type: String
    Description: '[WARNING] Should match with DeployUsingCodeBuild parameter. If parameter DeployUsingCodeBuild was enabled, then this parameter must be disabled!'
    AllowedValues: [ "Enabled", "Disabled" ]
    Default: 'Enabled'

Conditions:
  RunningInAnotherRegion: !Not [!Equals [!Ref ADPS3BucketRegion, !Ref "AWS::Region"]]
  EnableExternalAccessCondition: !And
    - !Equals [!Ref DnsZoneType, 'private']
    - !Equals [!Ref EnableExternalAccess, 'Enabled']
  UsingKubernetesModel: !Equals [!Ref UseKubernetesModel, "Enabled"]
  UsingSagemakerModel: !Equals [!Ref UseSagemakerModel, "Enabled"]
  UsingCodeBuild: !Equals [!Ref DeployUsingCodeBuild, "Enabled"]
  UsingLambda: !Equals [!Ref DeployUsingLambda, "Enabled"]

Resources:
  BasePlatformUtils:
    Type: AWS::CloudFormation::Stack
    Properties:
      Tags:
        - Key: 'Name'
          Value: 'BasePlatformUtils'
        - Key: 'Owner'
          Value: !ImportValue ADPPlatformOwner
        - Key: 'Project'
          Value: 'ADP'
      TemplateURL: !Sub 'https://${ADPS3BucketName}.s3.${ADPS3BucketRegion}.${AWS::URLSuffix}/platform/${BasePlatformVersion}/cloudformation/l0/base/utils.yaml'
      Parameters:
        ResourcePrefix: !ImportValue ADPResourcePrefix
        ADPS3BucketName: !Sub
          - '${BucketName}'
          - BucketName: !If [RunningInAnotherRegion, !ImportValue ADPS3BucketAnotherRegion, !Ref ADPS3BucketName]
        BasePlatformVersion: !Ref BasePlatformVersion

  BasePlatformInputs:
    DependsOn: BasePlatformUtils
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt BasePlatformUtils.Outputs.BasePlatformInputsLambdaArn
      BpStateBucket: !Sub
        - "${ResourcePrefix}-platform-state-${AWS::AccountId}"
        - ResourcePrefix: !ImportValue ADPResourcePrefix

  BasePlatformOutputs:
    DependsOn: BasePlatformUtils
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt BasePlatformUtils.Outputs.BasePlatformOutputsLambdaArn
      BpStateBucket: !Sub
        - "${ResourcePrefix}-platform-state-${AWS::AccountId}"
        - ResourcePrefix: !ImportValue ADPResourcePrefix

  BasePlatformCloudMapOutputs:
    DependsOn: BasePlatformOutputs
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt BasePlatformUtils.Outputs.BasePlatformOutputsLambdaArn
      StackName: "CloudMapStack"
      BpStateBucket: !Sub
        - "${ResourcePrefix}-platform-state-${AWS::AccountId}"
        - ResourcePrefix: !ImportValue ADPResourcePrefix

  BasePlatformVPCOutputs:
    DependsOn: BasePlatformOutputs
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt BasePlatformUtils.Outputs.BasePlatformOutputsLambdaArn
      StackName: "VPCStack"
      BpStateBucket: !Sub
        - "${ResourcePrefix}-platform-state-${AWS::AccountId}"
        - ResourcePrefix: !ImportValue ADPResourcePrefix

  BasePlatformLambdaFunctionsOutputs:
    DependsOn: BasePlatformVPCOutputs
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt BasePlatformUtils.Outputs.BasePlatformOutputsLambdaArn
      StackName: "BasePlatformLambdaFunctions"
      BpStateBucket: !Sub
        - "${ResourcePrefix}-platform-state-${AWS::AccountId}"
        - ResourcePrefix: !ImportValue ADPResourcePrefix

  BasePlatformKubernetes:
    DependsOn: BasePlatformVPCOutputs
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt BasePlatformUtils.Outputs.BasePlatformLambdaOperateArn
      BpStateBucket: !GetAtt BasePlatformUtils.Outputs.BasePlatformStateBucketName
      BpGdComponent: "mlops"
      BpAwsComponent: "Kubernetes"
      BpReferenceComponentName: "KubernetesStack"

  BasePlatformKubernetesExtra:
    DependsOn: BasePlatformKubernetes
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt BasePlatformUtils.Outputs.BasePlatformLambdaOperateArn
      BpStateBucket: !GetAtt BasePlatformUtils.Outputs.BasePlatformStateBucketName
      BpGdComponent: "mlops"
      BpAwsComponent: "KubernetesExtra"
      BpReferenceComponentName: "KubernetesExtraStack"

  BasePlatformMlflow:
    DependsOn: BasePlatformKubernetesExtra
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt BasePlatformUtils.Outputs.BasePlatformLambdaOperateArn
      BpStateBucket: !GetAtt BasePlatformUtils.Outputs.BasePlatformStateBucketName
      BpGdComponent: "mlops"
      BpAwsComponent: "Mlflow"
      BpReferenceComponentName: "MlflowStack"

  SageMakerNotebook:
    DependsOn: BasePlatformMlflow
    Type: AWS::CloudFormation::Stack
    Properties:
      Tags:
        - Key: 'Name'
          Value: 'SageMakerNotebook'
        - Key: 'Owner'
          Value: !ImportValue ADPPlatformOwner
        - Key: 'Project'
          Value: 'ADP'
      TemplateURL: !Sub 'https://${ADPS3BucketName}.s3.${ADPS3BucketRegion}.${AWS::URLSuffix}/platform/${BasePlatformVersion}/cloudformation/l1/ml/main.yaml'
      Parameters:
        DownloadFile: !Ref DownloadFile
        NotebookS3BucketName: !Ref S3BucketName
        NotebookInstanceName: !Ref NotebookInstanceName
        SubnetID: !GetAtt BasePlatformVPCOutputs.PrivateSubnet1AID
        VPCID: !GetAtt BasePlatformVPCOutputs.VPCID
        NotebookInstanceType: !Ref NotebookInstanceType
        MLEngineerGroupName: !GetAtt BasePlatformOutputs.MLEngineerGroupName
        DevOpsEngineerGroupName: !GetAtt BasePlatformOutputs.DevOpsEngineerGroupName
        GitHubRepository: !Ref GitHubRepository
        MlflowTrackingUri: !GetAtt BasePlatformMlflow.MlflowFQDN
        MlflowSetExperiment: !Ref MlflowSetExperiment
        ResourcePrefix: !ImportValue ADPResourcePrefix
        MlflowServiceImageRepository: !Ref MlflowServiceImageRepository

  ChurnPredictions:
    DependsOn: SageMakerNotebook
    Type: AWS::CloudFormation::Stack
    Properties:
      Tags:
        - Key: 'Name'
          Value: 'Churn-Predictions'
        - Key: 'Owner'
          Value: !ImportValue ADPPlatformOwner
        - Key: 'Project'
          Value: 'ADP'
      TemplateURL: !Sub 'https://${ADPS3BucketName}.s3.${ADPS3BucketRegion}.${AWS::URLSuffix}/platform/${BasePlatformVersion}/cloudformation/l2/case-mlops-eks.yaml'
      Parameters:
        ResourcePrefix: !ImportValue ADPResourcePrefix
        ADPS3BucketName: !Ref ADPS3BucketName
        UseCaseVersion: !Ref UseCaseVersion
        DockerRegistry: !Ref DockerRegistry
        DockerCPImage: !Ref DockerCPImage
        KubeManifestLambdaArn: !GetAtt BasePlatformKubernetes.KubeManifestLambdaArn
        KubeGetLambdaArn: !GetAtt BasePlatformKubernetes.KubeGetLambdaArn
        EKSClusterName: !GetAtt BasePlatformKubernetes.EKSClusterName
        ClusterOID: !GetAtt BasePlatformKubernetes.ClusterOID
        CloudMapNamespaceId: !GetAtt BasePlatformCloudMapOutputs.CloudMapNamespaceId
        Route53HostedZoneName: !GetAtt BasePlatformInputs.Route53HostedZoneName
        ChurnEndpointName: !If [ UsingSagemakerModel, !Ref ChurnEndpointName, "" ]
        ModelKubeEndpoint: !If [UsingKubernetesModel, "http://model-deployment.model-deployment.svc.cluster.local:8080", ""]
        RemoteAccessCIDR: !ImportValue ADPRemoteAccessCIDR
        DnsZoneType: !Ref DnsZoneType
        EnableExternalAccess: !Ref EnableExternalAccess

  MlAutomation:
    DependsOn: ChurnPredictions
    Type: AWS::CloudFormation::Stack
    Properties:
      Tags:
        - Key: 'Name'
          Value: 'MlAutomation'
        - Key: 'Owner'
          Value: !ImportValue ADPPlatformOwner
        - Key: 'Project'
          Value: 'ADP'
      TemplateURL: !Sub 'https://${ADPS3BucketName}.s3.${ADPS3BucketRegion}.${AWS::URLSuffix}/platform/${BasePlatformVersion}/cloudformation/l1/ml/ml-automation.yaml'
      Parameters:
        ResourcePrefix: !ImportValue ADPResourcePrefix
        ADPS3BucketName: !Sub
          - '${BucketName}'
          - BucketName: !If [ RunningInAnotherRegion, !ImportValue ADPS3BucketAnotherRegion, !Ref ADPS3BucketName ]
        BasePlatformVersion: !Ref BasePlatformVersion
        EmptyBucketLambdaArn: !GetAtt BasePlatformLambdaFunctionsOutputs.EmptyBucketLambdaArn
        BranchName: !Ref BranchName
        RepositoryID: !Ref RepositoryID
        GitConnectionName: !Ref GitConnectionName
        VPCID: !GetAtt BasePlatformVPCOutputs.VPCID
        SubnetID: !GetAtt BasePlatformVPCOutputs.PrivateSubnet1AID
        MlflowTrackingUri: !GetAtt BasePlatformMlflow.MlflowFQDN
        MlflowServiceImageRepository: !Ref MlflowServiceImageRepository
        MlflowArtifactS3Bucket: !GetAtt BasePlatformMlflow.MlflowArtifactS3Bucket
        ADPS3BucketRegion: !Ref ADPS3BucketRegion
        UseCaseVersion: !Ref UseCaseVersion
        DockerRegistry: !Ref DockerRegistry
        DockerMlModelImage: !Ref DockerMlModelImage
        UseKubernetesModel: !Ref UseKubernetesModel
        UseSagemakerModel: !Ref UseSagemakerModel
        DeployUsingCodeBuild: !Ref DeployUsingCodeBuild
        DeployUsingLambda: !Ref DeployUsingLambda
        CloudMapServiceId: !GetAtt ChurnPredictions.Outputs.MLOpsCloudMapService
        CloudMapInstanceName: !GetAtt ChurnPredictions.Outputs.MLOpsCloudMapInstance

  MlModelDeployment:
    DependsOn: MlAutomation
    Condition: UsingKubernetesModel
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Sub 'https://${ADPS3BucketName}.s3.${ADPS3BucketRegion}.${AWS::URLSuffix}/platform/${BasePlatformVersion}/cloudformation/l1/ml/ml-model-deployment.yaml'
      Parameters:
        ResourcePrefix: !ImportValue ADPResourcePrefix
        DefaultS3BucketName: !Ref ADPS3BucketName
        ADPS3BucketName: !Sub
          - '${BucketName}'
          - BucketName: !If [ RunningInAnotherRegion, !ImportValue ADPS3BucketAnotherRegion, !Ref ADPS3BucketName ]
        UseCaseVersion: !Ref UseCaseVersion
        DockerRegistry: !Ref DockerRegistry
        DockerMlModelImage: !Ref DockerMlModelImage
        KubeManifestLambdaArn: !GetAtt BasePlatformKubernetes.KubeManifestLambdaArn
        KubeGetLambdaArn: !GetAtt BasePlatformKubernetes.KubeGetLambdaArn
        EKSClusterName: !GetAtt BasePlatformKubernetes.EKSClusterName
        ClusterOID: !GetAtt BasePlatformKubernetes.ClusterOID
        Route53HostedZoneName: !GetAtt BasePlatformInputs.Route53HostedZoneName
        CloudMapInstanceName: !GetAtt ChurnPredictions.Outputs.MLOpsCloudMapInstance
        CloudMapServiceId: !GetAtt ChurnPredictions.Outputs.MLOpsCloudMapService

Outputs:
  ChurnPredictionsELB:
    Condition: EnableExternalAccessCondition
    Value: !GetAtt ChurnPredictions.Outputs.ChurnPredictionsELB
    Description: Churn Predictions service ELB address (will not work if external access is disabled)
  ChurnPredictionsFQDN:
    Value: !GetAtt ChurnPredictions.Outputs.ChurnPredictionsFQDN
    Description: Churn Predictions service DNS address (private zone will not be accessible form outside)

  NotebookInstanceName:
    Description: 'SageMaker Notebook Instance Name'
    Value: !Ref NotebookInstanceName
  NotebookURL:
    Description: "MLOps SageMaker Notebook Instance URL"
    Value: !Sub 'https://${AWS::Region}.console.aws.amazon.com/sagemaker/home?region=${AWS::Region}#/notebook-instances/openNotebook/${NotebookInstanceName}'

  MlflowFQDN:
    Description: 'Mlflow service DNS address (private zone will not be accessible form outside)'
    Value: !GetAtt BasePlatformMlflow.MlflowFQDN

  ADPAppPipeline:
    Condition: UsingCodeBuild
    Description: Name of the ADP Application Pipeline
    Value: !GetAtt MlAutomation.Outputs.ADPAppPipeline
