AWSTemplateFormatVersion: "2010-09-09"
Description: This template creates an Amazon VPC and subnet with the required configuration and an EC2 Instance
Parameters:
  paramKeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
    Type: 'AWS::EC2::KeyPair::KeyName'
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
  paramInstanceType:
    Description: EC2 instance specs configuration
    Type: String
    Default: t2.large
    AllowedValues:
      - t2.large
      - t2.xlarge
  paramPrefixS3Bucket:
    Description: Prefix of S3 bucket that will be created to store the certificates that will be created and these will be used later.
    Type: String
    Default: "aws-blog-beanstalk-gitlab-bucket"
  paramHostedZoneName:
    Description: Route 53 Hosted Zone  - For simplicity, we will be using the default value and do not change.
    Type: String
    Default: "example.com"
    AllowedValues:
      - example.com
  paramSubDomainName:
    Description: Sub domain name for the A record - For simplicity, we will be using the default value and do not change.
    Type: String
    Default: "runner"
    AllowedValues:
      - runner

Mappings:
  AMIs:
    us-east-1:
      Name: ami-06b263d6ceff0b3dd
    us-east-2:
      Name: ami-0010d386b82bc06f0

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: AWSBLOGBEANVPC
  PublicSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: "us-east-1a"
      MapPublicIpOnLaunch: 'True'
      Tags:
        - Key: Name
          Value: AWSBLOGBEANSubnetA
  PublicSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      CidrBlock: 10.0.2.0/24
      AvailabilityZone: "us-east-1b"
      MapPublicIpOnLaunch: 'True'
      Tags:
        - Key: Name
          Value: AWSBLOGBEANSubnetB
  PublicSubnetC:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref 'VPC'
      CidrBlock: 10.0.3.0/24
      AvailabilityZone: "us-east-1c"
      MapPublicIpOnLaunch: 'True'
      Tags:
        - Key: Name
          Value: AWSBLOGBEANSubnetC
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: AWSBLOGBEANGateway
  MyGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref 'InternetGateway'
      VpcId: !Ref 'VPC'
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref 'VPC'
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref 'PublicRouteTable'
      DestinationCidrBlock: '0.0.0.0/0'
      GatewayId: !Ref 'InternetGateway'
    DependsOn:
      - MyGatewayAttachment
  PublicSubnetRouteAssociationA:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref 'PublicRouteTable'
      SubnetId: !Ref 'PublicSubnetA'
  PublicSubnetRouteAssociationB:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref 'PublicRouteTable'
      SubnetId: !Ref 'PublicSubnetB'
  PublicSubnetRouteAssociationC:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref 'PublicRouteTable'
      SubnetId: !Ref 'PublicSubnetC'
  AWSBLOGBEANAccessSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: CloudFormationGroup
      VpcId: !Ref 'VPC'
      SecurityGroupIngress:
        - IpProtocol: '-1'
          CidrIp: "10.0.0.0/16"
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: "10.0.0.0/16"
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: "0.0.0.0/0"
        - IpProtocol: icmp
          FromPort: -1
          ToPort: -1
          CidrIp: "10.0.0.0/16"
      Tags:
        - Key: Name
          Value: AWSBLOGBEANVPCMasterSecurityGroup
  VPCDefaultSecurityGroupIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !GetAtt 'VPC.DefaultSecurityGroup'
      IpProtocol: '-1'
      CidrIp: 10.0.0.0/16

  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Join
        - "-"
        - - !Ref paramPrefixS3Bucket
          - !Select
            - 0
            - !Split
              - "-"
              - !Select
                - 2
                - !Split
                  - "/"
                  - !Ref "AWS::StackId"
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      AccessControl: BucketOwnerFullControl

  AWSBLOGBEANEC2InstanceRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /

  AWSBlogBeanstalkS3BucketsPolicy:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: AWS-BEANSTALK-BLOG-S3Buckets-Policy
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action:
              - 's3:*'
            # We can just use the bucket that will get created in this CF template.
            Resource: '*'
      Roles:
        - !Ref AWSBLOGBEANEC2InstanceRole

  AWSBlogBeanstalkElasticBeanstalkPolicy:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: AWS-BEANSTALK-BLOG-ElasticBeanstalk-Policy
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action:
              - 'elasticbeanstalk:*'
            # arn:aws:elasticbeanstalk:us-east-1:246997141225:applicationversion/SampleAWSElasticBeanstalkApplication/version-ymFeXCyH
            Resource: '*'
      Roles:
        - !Ref AWSBLOGBEANEC2InstanceRole

  AWSBlogBeanstalkCloudFormationPolicy:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: AWS-BEANSTALK-BLOG-CloudFormation-Policy
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action:
              - 'cloudformation:*'
            # cloudformation:GetTemplate on resource: arn:aws:cloudformation:us-east-1:246997141225:stack/awseb-e-3eizpfmjmb-stack/d5b4fc10-835c-11eb-b68a-0ef29e620f87
            Resource: '*'
      Roles:
        - !Ref AWSBLOGBEANEC2InstanceRole

  AWSBlogBeanstalkEC2Policy:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: AWS-BEANSTALK-BLOG-EC2-Policy
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action:
              - 'ec2:*'
            # Service:AmazonEC2, Message:You do not have permission to perform the 'ec2:DescribeSubnets' action.
            Resource: '*'
      Roles:
        - !Ref AWSBLOGBEANEC2InstanceRole

  AWSBlogBeanstalkCWLogsPolicy:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: AWS-BEANSTALK-BLOG-CWLOGS-Policy
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action:
              - 'logs:*'
            Resource: '*'
      Roles:
        - !Ref AWSBLOGBEANEC2InstanceRole

  AWSBlogBeanstalkAutoscalingPolicy:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: AWS-BEANSTALK-BLOG-AUTOSCALING-Policy
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action:
              - 'autoscaling:*'
            Resource: '*'
      Roles:
        - !Ref AWSBLOGBEANEC2InstanceRole

  AWSBlogBeanstalkELBPolicy:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: AWS-BEANSTALK-BLOG-ELBG-Policy
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action:
              - 'elasticloadbalancing:*'
            Resource: '*'
      Roles:
        - !Ref AWSBLOGBEANEC2InstanceRole

  AWSBlogBeanstalkIAMPolicy:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: AWS-BEANSTALK-BLOG-IAM-Policy
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action:
              - 'iam:*'
            Resource: '*'
      Roles:
        - !Ref AWSBLOGBEANEC2InstanceRole

  AWSBlogBeanstalkInstanceProfile:
    Type: 'AWS::IAM::InstanceProfile'
    Properties:
      Path: /
      Roles:
        - !Ref AWSBLOGBEANEC2InstanceRole

  GitLabEC2Instance:
    Type: "AWS::EC2::Instance"
    DependsOn: [VPC, PublicSubnetA, InternetGateway, PublicRoute, PublicSubnetRouteAssociationA, AWSBLOGBEANAccessSecurityGroup]
    CreationPolicy: # <--- creation policy with timeout of 5 minutes
      ResourceSignal:
        Timeout: PT20M
    Properties:
      InstanceType: !Ref paramInstanceType
      KeyName: !Ref paramKeyName
      IamInstanceProfile: !Ref AWSBlogBeanstalkInstanceProfile
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            VolumeType: "io1"
            VolumeSize: 30
            DeleteOnTermination: 'true'
            Encrypted: 'true'
            Iops: 300
      ImageId: !FindInMap
        - AMIs
        - !Ref 'AWS::Region'
        - Name
      NetworkInterfaces:
        - DeviceIndex: '0'
          AssociatePublicIpAddress: 'true'
          DeleteOnTermination: 'true'
          SubnetId: !Ref 'PublicSubnetA'
          GroupSet: [!GetAtt 'AWSBLOGBEANAccessSecurityGroup.GroupId']
      Tags:
        - Key: Name
          Value: AWSBlogBeanstalk-EC2Instance
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash -xe

            cd /home/ubuntu/
            touch test1
            sudo apt update -y
            sudo apt install awscli -y
            aws s3 cp s3://aws-bigdata-blog/artifacts/awsblog-beanstalk-gitlab/shell/gitlab-setup.sh .
            chmod -R 777 gitlab-setup.sh
            sudo apt-get -y install python-pip
            sudo pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz
            sh -x ./gitlab-setup.sh ${S3Bucket} ${paramSubDomainName}.${paramHostedZoneName} test > gitlab-setup-output.log
            # Start cfn-init
            /usr/local/bin/cfn-init --stack ${AWS::StackId} --resource GitLabEC2Instance --region ${AWS::Region}
            /usr/local/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource GitLabEC2Instance --region ${AWS::Region}
    Metadata:
      Comment: Install and setup gitlab
      AWS::CloudFormation::Init:
        config:
          commands:
            gitlab_setup:
              command: "echo 'COMPLETED'"

  AWSBlogBeanstalkRoute53HostedZone:
    Type: 'AWS::Route53::HostedZone'
    Properties:
      HostedZoneConfig:
        Comment: 'AWS Blog GitLab Beanstalk hosted zone'
      Name: !Ref paramHostedZoneName
      VPCs:
        -
          VPCId: !Ref 'VPC'
          VPCRegion: 'us-east-1'

  DnsRecord:
    Type: AWS::Route53::RecordSet
    DependsOn: [GitLabEC2Instance, AWSBlogBeanstalkRoute53HostedZone]
    Properties:
      HostedZoneName: !Join ['', [!Ref 'paramHostedZoneName', .]]
      Comment: DNS name for my instance.
      Name: !Join ['', [!Ref 'paramSubDomainName', ., !Ref 'paramHostedZoneName']]
      Type: A
      TTL: '900'
      ResourceRecords:
        - !GetAtt GitLabEC2Instance.PublicIp

Outputs:
  StackName:
    Value: !Ref 'AWS::StackName'
  SubnetIDA:
    Description: Public Subnet A.
    Value: !Ref 'PublicSubnetA'
    Export:
      Name: "awsblogbean-public-subnet-a"
  SubnetIDB:
    Description: Public Subnet B.
    Value: !Ref 'PublicSubnetB'
    Export:
      Name: "awsblogbean-public-subnet-b"
  SubnetIDC:
    Description: Public Subnet C.
    Value: !Ref 'PublicSubnetC'
    Export:
      Name: "awsblogbean-public-subnet-c"
  VPCSubnets:
    Description: All subnets
    Value:
      'Fn::Join': [",", [!Ref 'PublicSubnetA', !Ref 'PublicSubnetB', !Ref 'PublicSubnetC']]
    Export:
      Name: "VPCSubnetsList"
  AWSBLOGBEANAccessSecurityGroup:
    Description: Use this security group ID for all your services.
    Value: !GetAtt 'AWSBLOGBEANAccessSecurityGroup.GroupId'
    Export:
      Name: "awsblogbean--securitygroup-id"
  VPCID:
    Description: Use this VPC ID for all your services.
    Value: !Ref 'VPC'
    Export:
      Name: "awsblogbean-vpc-id"
  ExpS3Bucket:
    Description: S3 Bucket created in your account.
    Value: !Ref 'S3Bucket'
    Export:
      Name: "exp-s3-bucket"
  GitEc2PublicDNS:
    Description: GitLab Ec2 instance's Public Dns Name.
    Value: !GetAtt GitLabEC2Instance.PublicDnsName
    Export:
      Name: !Sub "${AWS::StackName}-PublicDnsName"
  GitEc2PublicIp:
    Description: GitLab Ec2 instance's Public Ip Address.
    Value: !GetAtt GitLabEC2Instance.PublicIp
    Export:
      Name: !Sub "public-ip-for-A-recordset"