AWSTemplateFormatVersion: 2010-09-09
Parameters:
  CodePipelineName:
    Type: String
    Description: (Required) Enter the name of the AWS CodePipeline pipeline to be created
  AmiId:
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
    Description: (Required) AMI Id used to launch the instances. This template uses AmazonLinux2 AMI using SSM parameter Store. To specify value, a parameter of 'aws:ec2:image' type must be created in AWS Systems Manager Parameter Store.
  # EC2KeypairName:
  #   Type: AWS::EC2::KeyPair::KeyName
  #   Description: (Required) Provide the name of the EC2 Keypair
Resources:

  #####
  # Network resources
  #####
  VPC:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: Test VPC

  InternetGateway:
    Type: 'AWS::EC2::InternetGateway'
    DependsOn: VPC
  AttachGateway:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  PublicSubnet1:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: true
      AvailabilityZone: !Select
        - '0'
        - Fn::GetAZs: !Ref 'AWS::Region'
      Tags:
        - Key: Name
          Value: Public Subnet 1
  PublicSubnet2:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.2.0/24
      MapPublicIpOnLaunch: true
      AvailabilityZone: !Select
        - '1'
        - Fn::GetAZs: !Ref 'AWS::Region'
      Tags:
        - Key: Name
          Value: Public Subnet 2

  PublicRouteTable:
    Type: 'AWS::EC2::RouteTable'
    DependsOn:
      - AttachGateway
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: Public Route Table
  PublicRoute:
    Type: 'AWS::EC2::Route'
    DependsOn:
      - VPC
      - AttachGateway
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnet1RouteTableAssociation:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable
  PublicSubnet2RouteTableAssociation:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable

  WebSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: Enable HTTP access
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: FINAL-Web-Security-Group
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0

  #####
  # CodePipeline resources
  #####
  CodePipelineArtifactStoreS3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      VersioningConfiguration:
        Status: Enabled

  CodePipelineArtifactStoreS3BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref CodePipelineArtifactStoreS3Bucket
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          -
            Sid: DenyUnEncryptedObjectUploads
            Effect: Deny
            Principal: '*'
            Action: s3:PutObject
            Resource: !Join [ '', [ !GetAtt CodePipelineArtifactStoreS3Bucket.Arn, '/*' ] ]
            Condition:
              StringNotEquals:
                s3:x-amz-server-side-encryption: aws:kms
          -
            Sid: DenyInsecureConnections
            Effect: Deny
            Principal: '*'
            Action: s3:*
            Resource: !Join [ '', [ !GetAtt CodePipelineArtifactStoreS3Bucket.Arn, '/*' ] ]
            Condition:
              Bool:
                aws:SecureTransport: false

  CodePipelineServiceRole:
    Type: "AWS::IAM::Role"
    DependsOn: CodeDeployServiceRole
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "codepipeline.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Policies:
        - PolicyName: CodeDeployPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Action:
                  - "codedeploy:CreateDeployment"
                  - "codedeploy:GetApplication"
                  - "codedeploy:GetApplicationRevision"
                  - "codedeploy:GetDeployment"
                  - "codedeploy:GetDeploymentConfig"
                  - "codedeploy:RegisterApplicationRevision"
                Effect: Allow
                Resource: '*'
        - PolicyName: S3Permissions
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Action:
                  - "s3:PutObject"
                  - "s3:GetObjectAcl"
                  - "s3:GetObject"
                  - "s3:ListBucket"
                  - "s3:GetBucketVersioning"
                  - "s3:GetBucketAcl"
                  - "s3:GetBucketLocation"
                  - "s3:GetObjectVersion"
                Effect: Allow
                Resource: '*'
        - PolicyName: CodeCommitPermissions
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Action:
                  - codecommit:CancelUploadArchive
                  - codecommit:GetBranch
                  - codecommit:GetCommit
                  - codecommit:GetRepository
                  - codecommit:GetUploadArchiveStatus
                  - codecommit:UploadArchive
                Effect: Allow
                Resource: '*'
        - PolicyName: CodeBuildPermissions
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Action:
                  - codebuild:BatchGetBuilds
                  - codebuild:StartBuild
                  - codebuild:BatchGetBuildBatches
                  - codebuild:StartBuildBatch
                Effect: Allow
                Resource: '*'

  SampleCodePipeline:
    Type: AWS::CodePipeline::Pipeline
    DependsOn: [ CodeCommitRepo ]
    Properties:
      ArtifactStore:
        Type: S3
        Location: !Ref CodePipelineArtifactStoreS3Bucket
      Name: !Ref CodePipelineName
      RoleArn: !GetAtt CodePipelineServiceRole.Arn
      Stages:
        - Name: Source
          Actions:
            - Name: Source
              ActionTypeId:
                Category: Source
                Owner: AWS
                Version: "1"
                Provider: CodeCommit
              OutputArtifacts:
                - Name: SourceArtifact
              Configuration:
                RepositoryName: 'NewsletterRepo'
                BranchName: 'main'
        - Name: Deploy
          Actions:
            - Name: Deploy
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: "1"
                Provider: CodeDeploy
              Configuration:
                ApplicationName: !Ref CodeDeployApplication
                DeploymentGroupName: !Ref DeploymentGroup
              InputArtifacts:
                - Name: SourceArtifact
        - Name: AcceptanceTest
          Actions:
            - Name: AcceptanceTest
              ActionTypeId:
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: "1"
              Configuration:
                ProjectName: NewsletterBuild
              InputArtifacts:
                - Name: SourceArtifact
              OutputArtifacts:
                - Name: BuildArtifact

  #####
  # EC2 resources
  #####
  EC2InstanceProfileRoleForCodeDeploy:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "ec2.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforAWSCodeDeploy"
        - "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
        - "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"

  EC2InstanceprofileforCodeDeploy:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref EC2InstanceProfileRoleForCodeDeploy

  EC2Instance:
    Type: AWS::EC2::Instance
    CreationPolicy:
      ResourceSignal:
        Timeout: PT15M
    Properties:
      IamInstanceProfile: !Ref EC2InstanceprofileforCodeDeploy
      ImageId: !Ref AmiId
      InstanceType: t2.micro
      # KeyName: !Ref EC2KeypairName
      SecurityGroupIds:
        - !Ref WebSecurityGroup
      SubnetId: !Ref PublicSubnet1
      Tags:
        - Key: Name
          Value: CodePipelineBlog
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash -xe
            yum -y update
            yum install -y ruby
            yum install -y aws-cli
            cd /home/ec2-user
            #Downloading & Installing CodeDeploy Agent as per https://docs.aws.amazon.com/codepipeline/latest/userguide/tutorials-simple-s3.html#S3-create-instances
            aws s3 cp s3://aws-codedeploy-${AWS::Region}/latest/install . --region ${AWS::Region}
            chmod +x ./install
            ./install auto
            #Sending signal to CloudFormation
            /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource EC2Instance --region ${AWS::Region}

  #####
  # CodeDeploy Resources
  #####
  CodeDeployServiceRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "codedeploy.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole"

  CodeDeployApplication:
    Type: AWS::CodeDeploy::Application
    DependsOn: EC2Instance
    Properties:
      ApplicationName: CodePipeBlogSampleApplication
      ComputePlatform: Server

  DeploymentGroup:
    Type: AWS::CodeDeploy::DeploymentGroup
    Properties:
      ApplicationName: !Ref CodeDeployApplication
      DeploymentGroupName: MyDemoDeploymentGroup
      DeploymentConfigName: CodeDeployDefault.AllAtOnce
      DeploymentStyle:
          DeploymentOption: WITHOUT_TRAFFIC_CONTROL
          DeploymentType: IN_PLACE
      Ec2TagFilters:
        - Key: Name
          Value: CodePipelineBlog
          Type: KEY_AND_VALUE
      ServiceRoleArn: !GetAtt CodeDeployServiceRole.Arn

  #####
  # CodeCommit repository
  #####
  CodeCommitRepo:
    Type: AWS::CodeCommit::Repository
    Properties:
      RepositoryName: NewsletterRepo
      RepositoryDescription: This is a repository for my project with code from a source S3 bucket.
      Code:
        BranchName: main
        S3:
          Bucket: aws-tc-largeobjects
          Key: DEV-AWS-MO-DevOps-C2/downloads/Blog.zip

  #####
  # CodeBuild Resources
  #####
  CodeBuildPolicy:
    Description: Setting IAM policy for service role for CodeBuild
    Properties:
      PolicyDocument:
        Statement:
        - Action:
          - logs:CreateLogGroup
          - logs:CreateLogStream
          - logs:PutLogEvents
          Effect: Allow
          Resource: '*'
        - Action:
          - ec2:Describe*
          Effect: Allow
          Resource: '*'
        - Action:
          - s3:PutObject
          - s3:GetObject
          - s3:GetObjectVersion
          Effect: Allow
          Resource:
          - !Join
            - ''
            - - 'arn:aws:s3:::'
              - !Ref 'CodePipelineArtifactStoreS3Bucket'
          - !Join
            - ''
            - - 'arn:aws:s3:::'
              - !Ref 'CodePipelineArtifactStoreS3Bucket'
              - /*
      PolicyName: !Join
        - '-'
        -  - !Ref 'AWS::StackName'
           - CodeBuildPolicy2
      Roles:
      - !Ref 'CodeBuildRole'
    Type: AWS::IAM::Policy

  CodeBuildProject:
    DependsOn:
    - CodeBuildPolicy
    Properties:
      Artifacts:
        Type: NO_ARTIFACTS
      BadgeEnabled: true
      Description: !Join
        - ''
        - - 'CodeBuild Project for '
          - !Ref 'AWS::StackName'
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
        Type: LINUX_CONTAINER
      Name: NewsletterBuild
      ServiceRole: !Ref 'CodeBuildRole'
      Source:
        Type: CODECOMMIT
        BuildSpec: 'acctests-buildspec.yml'
        Location: !Join
          - ''
          - - 'https://git-codecommit.'
            - !Ref 'AWS::Region'
            - '.amazonaws.com/v1/repos/NewsletterRepo'
    Type: AWS::CodeBuild::Project

  CodeBuildRole:
    Description: Creating service role in IAM for AWS CodeBuild
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Action: sts:AssumeRole
          Effect: Allow
          Principal:
            Service: codebuild.amazonaws.com
      Path: /
      RoleName: !Join
        - '-'
        - - !Ref 'AWS::StackName'
          - CodeBuild
    Type: AWS::IAM::Role

Outputs:
  EC2InstancePublicDNSName:
    Description: Public DNS name of the launched EC2 instance
    Value: !GetAtt EC2Instance.PublicDnsName
    Export:
      Name: EC2InstancePublicDNSName
  EC2InstanceId:
    Description: Instance ID of the launched EC2 instance
    Value:  !Ref EC2Instance
    Export:
      Name: EC2InstanceId
