# * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# *
# * Permission is hereby granted, free of charge, to any person obtaining a copy of this
# * software and associated documentation files (the "Software"), to deal in the Software
# * without restriction, including without limitation the rights to use, copy, modify,
# * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
# * permit persons to whom the Software is furnished to do so.
# *
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

AWSTemplateFormatVersion: 2010-09-09
Description: Service Catalog Demo - Provision Web Server from Service Catalog
Parameters:
  TopicName:
    Description: Notification Topic
    Type: String
    Default: 'sc-demo-sns-topic'
  PortList:
    Description: 'Comma-delimited list if ALB ports: WebALBIn,WebEC2In'
    Type: CommaDelimitedList
    Default: 443,443
  ALBHealthCheckList:
    Description: 'Comma delimited list of ALB health check: path,port,protocol'
    Type: CommaDelimitedList
    Default: /healthcheck.html,443,HTTPS
  ALBHealthCheckThresholdsList:
    Description: 'Comma delimited list of ALB health check thresholds: HealthyThreshold,UnhealthyThreshold,IntervalThreshold,TimeoutThreshold'
    Type: CommaDelimitedList
    Default: 2,5,5,2
  AppInstanceType:
    Description: EC2 instance type
    Type: String
    Default: t2.medium
    AllowedValues:
      - t2.micro
      - t2.small
      - t2.medium
      - t2.large
  AppHealthCheckGracePeriod:
    Description: Number of seconds after instance launch ALB begins health checks
    Type: Number
    MinValue: 40
    MaxValue: 5000
    Default: 300
    ConstraintDescription: Value must be between 40-5000 seconds
  HealthCheckType:
    Description: The service you want the health status from, Amazon EC2 or Elastic Load Balancer
    Type: String
    Default: ELB
    AllowedValues:
      - ELB
      - EC2
  AppMinMaxCount:
    Description: 'Comma delimited list of min then max number of EC2 instances for ASG: min,max'
    Type: CommaDelimitedList
    Default: 1,2
  ScalePolicyList:
    Description: 'Comma delimited list of AutoScaling policy: ScaleOutCooldown,ScaleOutAdjustment,ScaleInCooldown,ScaleInAdjustment'
    Type: CommaDelimitedList
    Default: 3600,1,1200,-1
  EnableScalePolicy:
    Description: Enable Auto Scaling Policy
    Type: String
    Default: false
    AllowedValues:
      - true
      - false
  ImageId:
    Description: AMI ImageId
    Type: String
    Default: 'ami-0de53d8956e8dcf80'
  DomainName:
    Description: Domain Name
    Type: String
    Default: 'www.example.com'
  BucketName:
    Description: S3 Bucket Name
    Type: String
  KMSAlias:
    Description: Alias for KMS Key
    Type: String
    Default: 'sc-demo-key'

Resources:
  WebResourceSelector:
    Type: "Custom::ResourceSelector"
    Version: "1.0"
    Properties:
      ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:sc-resource-selector'
      Options:
        OnError: failed
      Resources:
        vpc:
          Tags:
            - Key: Name
              Value: sc-demo-vpc
          Options:
            Match: all
            Output: single
        subnet:
          Tags:
            - Key: Name
              Value: sc-demo-web-subnet
          Options:
            Match: all
            Output: all
        sg:
          Tags:
            - Key: Name
              Value: sc-demo-web-sg
          Options:
            Match: all
            Output: single
            GroupName: sc-demo-web-sg
        acm:
          Tags:
            - Key: Name
              Value: sc-demo
          Options:
            Match: all
            Output: single
            Domain: !Ref DomainName

  ProductSelectorKMS:
    Type: "Custom::ProductSelector"
    Version: "1.0"
    Properties:
      ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:sc-product-selector'
      ProductName: kms
  ProductSelectorALB:
    Type: "Custom::ProductSelector"
    Version: "1.0"
    Properties:
      ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:sc-product-selector'
      ProductName: alb
  ProductSelectorALBTarget:
    Type: "Custom::ProductSelector"
    Version: "1.0"
    Properties:
      ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:sc-product-selector'
      ProductName: albtarget
  ProductSelectorALBListener:
    Type: "Custom::ProductSelector"
    Version: "1.0"
    Properties:
      ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:sc-product-selector'
      ProductName: alblistener
  ProductSelectorAutoScaling:
    Type: "Custom::ProductSelector"
    Version: "1.0"
    Properties:
      ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:sc-product-selector'
      ProductName: autoscaling
  ProductSelectorSNS:
    Type: "Custom::ProductSelector"
    Version: "1.0"
    Properties:
      ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:sc-product-selector'
      ProductName: sns
  ProductSelectorS3:
    Type: "Custom::ProductSelector"
    Version: "1.0"
    Properties:
      ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:sc-product-selector'
      ProductName: s3
  ProductSelectorFirehose:
    Type: "Custom::ProductSelector"
    Version: "1.0"
    Properties:
      ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:sc-product-selector'
      ProductName: firehose

  KinesisRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: 'sc-demo-kinesis-role'
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Effect: "Allow"
            Principal:
              Service:
                - "firehose.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      Policies:
        -
          PolicyName: "FH"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action:
                  - logs:PutLogEvents
                Resource: "*"
              -
                Effect: "Allow"
                Action:
                  - s3:AbortMultipartUpload
                  - s3:GetBucketLocation
                  - s3:GetObject
                  - s3:ListBucket
                  - s3:ListBucketMultipartUploads
                  - s3:PutObject
                Resource: "*"

  EC2Role:
    Type: "AWS::IAM::Role"
    DependsOn: KinesisRole
    Properties:
      RoleName: 'sc-demo-ec2-role'
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Effect: "Allow"
            Principal:
              Service:
                - "ec2.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      Policies:
        -
          PolicyName: "Stream"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action: 
                  - s3:GetBucketLocation
                  - s3:GetObject
                  - s3:ListBucket                
                Resource: "*"
              -
                Effect: "Allow"
                Action:
                  - firehose:DeleteDeliveryStream
                  - firehose:PutRecord
                  - firehose:PutRecordBatch
                  - firehose:UpdateDestination
                Resource: "*"

  EC2InstanceProfile:
    Type: "AWS::IAM::InstanceProfile"
    DependsOn: EC2Role
    Properties:
      Path: "/"
      InstanceProfileName: sc-demo-ec2-role
      Roles:
        -
          Ref: "EC2Role"

  KMSKey:
    Type: "AWS::ServiceCatalog::CloudFormationProvisionedProduct"
    DependsOn: EC2InstanceProfile
    Properties:
      ProvisionedProductName: 'sc-demo-kms'
      ProvisioningParameters:
        - Key: KeyAlies
          Value: !Ref KMSAlias
        - Key: Principals
          Value: 'arn:aws:iam::{accountid}:root,arn:aws:iam::{accountid}:role/sc-kms-product-role,arn:aws:iam::{accountid}:role/sc-elasticsearch-product-role,arn:aws:iam::{accountid}:role/sc-sns-product-role,arn:aws:iam::{accountid}:role/sc-autoscaling-product-role,arn:aws:iam::{accountid}:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling,arn:aws:iam::{accountid}:role/sc-demo-kinesis-role'
      ProductId: !GetAtt ProductSelectorKMS.ProductId
      ProvisioningArtifactId: !GetAtt ProductSelectorKMS.ArtifactId

  S3Bucket:
    Type: "AWS::ServiceCatalog::CloudFormationProvisionedProduct"
    DependsOn: KMSKey
    Properties:
      ProvisionedProductName: 'sc-demo-s3'
      ProvisioningParameters:
        - Key: BucketName
          Value: !Ref BucketName
        - Key: KMSId
          Value: !GetAtt KMSKey.Outputs.KMSId
      ProductId: !GetAtt ProductSelectorS3.ProductId
      ProvisioningArtifactId: !GetAtt ProductSelectorS3.ArtifactId

  Kinesis:
    Type: "AWS::ServiceCatalog::CloudFormationProvisionedProduct"
    DependsOn: S3Bucket
    Properties:
      ProvisionedProductName: 'sc-demo-kinesis-fh-stream'
      ProvisioningParameters:
        - Key: Name
          Value: sc-lab-kinesis-fh-stream
        - Key: BucketArn
          Value: !GetAtt S3Bucket.Outputs.S3BucketArn
        - Key: RoleArn
          Value: !GetAtt KinesisRole.Arn
        - Key: KMSArn
          Value: !GetAtt KMSKey.Outputs.KMSArn
      ProductId: !GetAtt ProductSelectorFirehose.ProductId
      ProvisioningArtifactId: !GetAtt ProductSelectorFirehose.ArtifactId

  SNS:
    Type: "AWS::ServiceCatalog::CloudFormationProvisionedProduct"
    DependsOn: Kinesis
    Properties:
      ProvisionedProductName: 'sc-demo-sns'
      ProvisioningParameters:
        - Key: TopicName
          Value: !Ref TopicName
        - Key: KMSId
          Value: !GetAtt KMSKey.Outputs.KMSId
        - Key: PolicyPrincipal
          Value: !Sub 'arn:aws:iam::${AWS::AccountId}:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling'
        - Key: PolicyAction
          Value: "SNS:Publish"
      ProductId: !GetAtt ProductSelectorSNS.ProductId
      ProvisioningArtifactId: !GetAtt ProductSelectorSNS.ArtifactId

  ALB:
    Type: "AWS::ServiceCatalog::CloudFormationProvisionedProduct"
    DependsOn: SNS
    Properties:
      ProvisionedProductName: 'sc-demo-alb'
      ProvisioningParameters:
        - Key: ALBName
          Value: sc-demo-alb
        - Key: SecurityGroupIds
          Value: !GetAtt WebResourceSelector.sg
        - Key: SubnetIds
          Value: !GetAtt WebResourceSelector.subnet
      ProductId: !GetAtt ProductSelectorALB.ProductId
      ProvisioningArtifactId: !GetAtt ProductSelectorALB.ArtifactId

  ALBTarget:
    Type: "AWS::ServiceCatalog::CloudFormationProvisionedProduct"
    DependsOn: ALB
    Properties:
      ProvisionedProductName: 'sc-demo-alb-target'
      ProvisioningParameters:
        - Key: ALBTargetName
          Value: sc-demo-alb-target
        - Key: HealthCheckIntervalSeconds
          Value: !Select [2, !Ref ALBHealthCheckThresholdsList]
        - Key: HealthCheckPath
          Value: !Select [0, !Ref ALBHealthCheckList]
        - Key: HealthCheckPort
          Value: !Select [1, !Ref ALBHealthCheckList]
        - Key: HealthCheckProtocol
          Value: !Select [2, !Ref ALBHealthCheckList]
        - Key: HealthCheckTimeoutSeconds
          Value: !Select [3, !Ref ALBHealthCheckThresholdsList]
        - Key: HealthyThresholdCount
          Value: !Select [1, !Ref ALBHealthCheckThresholdsList]
        - Key: AppPort
          Value: !Select [1, !Ref PortList]
        - Key: UnhealthyThresholdCount
          Value: !Select [0, !Ref ALBHealthCheckThresholdsList]
        - Key: VpcId
          Value: !GetAtt WebResourceSelector.vpc
      ProductId: !GetAtt ProductSelectorALBTarget.ProductId
      ProvisioningArtifactId: !GetAtt ProductSelectorALBTarget.ArtifactId

  ALBListener:
    Type: "AWS::ServiceCatalog::CloudFormationProvisionedProduct"
    DependsOn: ALBTarget
    Properties:
      ProvisionedProductName: 'sc-demo-alb-listener'
      ProvisioningParameters:
        - Key: CertificateArn
          Value: !GetAtt WebResourceSelector.acm
        - Key: ALBTargetGroupArn
          Value: !GetAtt ALBTarget.Outputs.ALBTargetId
        - Key: ALBArn
          Value: !GetAtt ALB.Outputs.ALBArn
        - Key: AppPort
          Value: !Select [0, !Ref PortList]
      ProductId: !GetAtt ProductSelectorALBListener.ProductId
      ProvisioningArtifactId: !GetAtt ProductSelectorALBListener.ArtifactId

  ASC:
    Type: "AWS::ServiceCatalog::CloudFormationProvisionedProduct"
    DependsOn: ALBListener
    Properties:
      ProvisionedProductName: 'sc-demo-autoscaling'
      ProvisioningParameters:
        - Key: ALBTargetGroupArn
          Value: !GetAtt ALBTarget.Outputs.ALBTargetId
        - Key: AppInstanceType
          Value: !Ref AppInstanceType
        - Key: AppEC2IAMRole
          Value: 'sc-demo-ec2-role'
        - Key: AppHealthCheckGracePeriod
          Value: !Ref AppHealthCheckGracePeriod
        - Key: HealthCheckType
          Value: !Ref HealthCheckType
        - Key: AppMinCount
          Value: !Select [0, !Ref AppMinMaxCount]
        - Key: AppMaxCount
          Value: !Select [1, !Ref AppMinMaxCount]
        - Key: SNSTopicARN
          Value: !GetAtt SNS.Outputs.SNSArn
        - Key: EnableScalePolicy
          Value: !Ref EnableScalePolicy
        - Key: ScaleOutCooldown
          Value: !Select [0, !Ref ScalePolicyList]
        - Key: ScaleOutAdjustment
          Value: !Select [1, !Ref ScalePolicyList]
        - Key: ScaleInCooldown
          Value: !Select [2, !Ref ScalePolicyList]
        - Key: ScaleInAdjustment
          Value: !Select [3, !Ref ScalePolicyList]
        - Key: ImageId
          Value: !Ref ImageId
        - Key: SecurityGroupIds
          Value: !GetAtt WebResourceSelector.sg
        - Key: SubnetIds
          Value: !GetAtt WebResourceSelector.subnet
        - Key: UserData
          Value:
            Fn::Base64:
              !Sub |
                #!/bin/bash
                yum update -y;
                wget  https://s3.amazonaws.com/aws-service-catalog-reference-architectures/labs/preventive-control/httpd.conf -O /tmp/httpd.conf;
                wget  https://s3.amazonaws.com/aws-service-catalog-reference-architectures/labs/preventive-control/ssl.conf -O /tmp/ssl.conf;
                wget  https://s3.amazonaws.com/aws-service-catalog-reference-architectures/labs/preventive-control/fh-agent.json -O /tmp/fh-agent.json;
                yum install https://s3.amazonaws.com/streaming-data-agent/aws-kinesis-agent-latest.amzn1.noarch.rpm -y;
                yum install httpd mod_ssl -y;
                echo "<html>Success</html>" >> /var/www/html/healthcheck.html;
                chmod 755 /var/www;
                chgrp -R apache /var/log/httpd;
                chmod -R g+wr /var/log/httpd;
                mv /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.bak || true;
                cp /tmp/httpd.conf /etc/httpd/conf/httpd.conf;
                mv /etc/httpd/conf.d/ssl.conf /etc/httpd/conf.d/ssl.bak || true;
                cp /tmp/ssl.conf /etc/httpd/conf.d/ssl.conf;
                sed -i "s/localhost/${DomainName}/g" /etc/httpd/conf.d/ssl.conf;
                mkdir /etc/pki/tls/apache;
                cd /etc/pki/tls/apache;
                chgrp -R apache /etc/pki/tls/apache;
                chmod -R g+wr /etc/pki/tls/apache;
                openssl req -newkey rsa:2048 -nodes -x509 -keyout ./myown.key -out ./myselfsigned.crt -days 180 -subj "/C=US/ST=Massachusetts/L=Boston/O=Demo/CN=${DomainName}";
                service httpd start;
                chkconfig httpd on;
                mv /etc/aws-kinesis/agent.json /etc/aws-kinesis/agent.bak || true;
                cp /tmp/fh-agent.json /etc/aws-kinesis/agent.json;
                chmod 777 /var/log/httpd;
                service aws-kinesis-agent start;
                ab -n 50000 -c 20 https://localhost/healthcheck.html

      ProductId: !GetAtt ProductSelectorAutoScaling.ProductId
      ProvisioningArtifactId: !GetAtt ProductSelectorAutoScaling.ArtifactId