Untitled

 avatar
unknown
plain_text
3 years ago
37 kB
5
Indexable
Resources:
  # 01 Create VPC
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: "10.0.0.0/16"
      EnableDnsHostnames: true
  InternetGateway:
    Type: AWS::EC2::InternetGateway
  GatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  # 2 Add Route Table
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
      RouteTableId: !Ref PublicRouteTable

  ## 3 Add Public Subnets
  PublicSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]]
      CidrBlock: "10.0.0.0/24"
  PublicSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetA
      RouteTableId: !Ref PublicRouteTable
  PublicSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]]
      CidrBlock: "10.0.1.0/24"
  PublicSubnetBRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetB
      RouteTableId: !Ref PublicRouteTable

  # 4 Add NAT
  EIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
  NAT:
    DependsOn: GatewayAttachment
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !Sub "${EIP.AllocationId}"
      SubnetId: !Ref PublicSubnetA

  # 5 Add private Route Table
  PrivateSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: VPC
  PrivateRouteTableRouteNAT:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateSubnetRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NAT

  # 6 Add private subnets and associate route tables
  PrivateSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]]
      CidrBlock: "10.0.64.0/24"
  PrivateSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnetA
      RouteTableId: !Ref PrivateSubnetRouteTable
  PrivateSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]]
      CidrBlock: "10.0.65.0/24"
  PrivateSubnetBRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnetB
      RouteTableId: !Ref PrivateSubnetRouteTable

  # 7 add data route without any rules
  DataSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC

  DataSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]]
      CidrBlock: "10.0.128.0/24"
  DataSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref DataSubnetA
      RouteTableId: !Ref DataSubnetRouteTable
  DataSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]]
      CidrBlock: "10.0.129.0/24"
  DataSubnetBRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref DataSubnetB
      RouteTableId: !Ref DataSubnetRouteTable

  # 8 Security Groups

  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for ALB"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      VpcId: !Ref VPC
  WebSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for Web Servers"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref ALBSecurityGroup
      VpcId: !Ref VPC
  EFSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for EFS"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 2049
          ToPort: 2049
          SourceSecurityGroupId: !Ref WebSecurityGroup
      VpcId: !Ref VPC
  ElasticacheSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for ElastiCache"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 11211
          ToPort: 11211
          SourceSecurityGroupId: !Ref WebSecurityGroup
      VpcId: !Ref VPC
  DatabaseSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for Amazon RDS cluster"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !Ref WebSecurityGroup
      VpcId: !Ref VPC


  # 9 ALB
  ALB: 
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      Subnets: 
        - !Ref PublicSubnetA
        - !Ref PublicSubnetB
      LoadBalancerAttributes:
      - Key: idle_timeout.timeout_seconds
        Value: "60"
      SecurityGroups:
      - !Ref ALBSecurityGroup
  ALBListener:
    Type : AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
      - Type: forward
        TargetGroupArn: !Ref ALBTargetGroup
      LoadBalancerArn: !Ref ALB
      Port: 80
      Protocol: HTTP
  ALBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 30
      HealthCheckPath: /wp-login.php
      
      HealthCheckTimeoutSeconds: 5
      Port: 80
      Protocol: HTTP
      UnhealthyThresholdCount: 5
      VpcId: !Ref VPC

  # 10 Add ElastiCache

  ElastiCacheSubnetGroup: 
    Type: AWS::ElastiCache::SubnetGroup
    Properties: 
      Description: ElastiCache Subnet Group for WordPress
      SubnetIds:
        - !Ref DataSubnetA
        - !Ref DataSubnetB
  ElastiCacheCluster:
    Type: AWS::ElastiCache::CacheCluster
    Properties:
      CacheNodeType: 'cache.t2.micro'
      CacheSubnetGroupName: !Ref ElastiCacheSubnetGroup
      Engine: memcached
      NumCacheNodes: 1
      VpcSecurityGroupIds:
      - !Ref ElasticacheSecurityGroup
  
  # 11 Database Cluster

  DataSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: "RDS Database Subnet Group for WordPress"
      SubnetIds: 
        - !Ref DataSubnetA
        - !Ref DataSubnetB
  DatabaseCluster:
    Type: AWS::RDS::DBCluster
    Properties:
      MasterUsername: "wordpress"
      MasterUserPassword: "supersecret"
      DatabaseName: "wordpressdb"
      Engine: aurora
      Port: 3306
      EngineMode: serverless
      ScalingConfiguration:
        AutoPause: false
        MaxCapacity: 16
        MinCapacity: 2
      DBSubnetGroupName: !Ref DataSubnetGroup
      VpcSecurityGroupIds:
      - !Ref DatabaseSecurityGroup

  # 12 EFS

  EFS:
    Type: AWS::EFS::FileSystem
    Properties:
      PerformanceMode: 'maxIO'
  EFSMountTargetA:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: !Ref EFS
      SecurityGroups:
        - !Ref EFSSecurityGroup
      SubnetId: !Ref DataSubnetA
  EFSMountTargetB:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: !Ref EFS
      SecurityGroups:
        - !Ref EFSSecurityGroup
      SubnetId: !Ref DataSubnetB

  # 13 Instance Profile

  WebInstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
        - Effect: Allow
          Principal:
            Service: ec2.amazonaws.com
          Action: sts:AssumeRole
      Policies:
      - PolicyName: logs
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action:
            - logs:CreateLogGroup
            - logs:CreateLogStream
            - logs:PutLogEvents
            - logs:DescribeLogStreams
            Resource:
            - arn:aws:logs:*:*:*
  WebInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
      - !Ref WebInstanceRole

  ## 14 AutoScaling

  WebLaunchConfiguration:
    Type: AWS::AutoScaling::LaunchConfiguration
    Properties:
      IamInstanceProfile: !Ref WebInstanceProfile
      ImageId: "ami-1a962263"
      InstanceMonitoring: true
      InstanceType: "t3.xlarge"
      SecurityGroups:
      - !Ref WebSecurityGroup
      UserData:
        "Fn::Base64":
          !Sub |
            #!/bin/bash -xe
            yum update -y
            mkdir -p /var/www/wordpress
            mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 ${EFS}.efs.${AWS::Region}.amazonaws.com:/ /var/www/wordpress
            /opt/aws/bin/cfn-init --configsets deploy_webserver --verbose --stack ${AWS::StackName} --resource WebLaunchConfiguration --region ${AWS::Region}
            /opt/aws/bin/cfn-signal --exit-code $? --stack ${AWS::StackName} --resource WebAutoScalingGroup --region ${AWS::Region}
    Metadata:
      AWS::CloudFormation::Init:
        configSets:
          deploy_webserver:
            - install_webserver
            - build_cacheclient
            - build_wordpress
            - build_opcache
            - download_aws_ini
            - install_aws_ini
            - install_cacheclient
            - install_wordpress
            - install_opcache
            - start_webserver
        install_webserver:
          packages:
            yum:
              awslogs: []
              httpd24: []
              mysql56: []
              php70: []
              php70-devel: []
              php7-pear: []
              php70-mysqlnd: []
          files:
            /tmp/create_site_conf.sh:
              content:
                !Join [
                  "",[
                    "#!/bin/bash -xe\n",
                    "if [ ! -f /etc/httpd/conf.d/wordpress.conf ]; then\n",                    
                    "   touch /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo 'ServerName 127.0.0.1:80' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo 'DocumentRoot /var/www/wordpress/wordpress' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo '<Directory /var/www/wordpress/wordpress>' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo '  Options Indexes FollowSymLinks' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo '  AllowOverride All' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo '  Require all granted' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo '</Directory>' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "fi\n"
                  ]
                ]
              mode: 000500
              owner: root
              group: root
          commands:
            create_site_conf:
              command: ./create_site_conf.sh
              cwd: /tmp
              ignoreErrors: false
        build_cacheclient:
          packages:
            yum:
              gcc-c++: []  
          files:
            /tmp/install_cacheclient.sh:
              content: |
                  #!/bin/bash -xe
                  ln -s /usr/bin/pecl7 /usr/bin/pecl #just so pecl is available easily
                  pecl7 install igbinary
                  wget -P /tmp/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/AmazonElastiCacheClusterClient-2.0.1-PHP70-64bit.tar.gz
                  tar -xf '/tmp/AmazonElastiCacheClusterClient-2.0.1-PHP70-64bit.tar.gz'
                  cp '/tmp/artifact/amazon-elasticache-cluster-client.so' /usr/lib64/php/7.0/modules/
                  if [ ! -f /etc/php-7.0.d/50-memcached.ini ]; then
                      touch /etc/php-7.0.d/50-memcached.ini
                  fi
                  echo 'extension=igbinary.so;' >> /etc/php-7.0.d/50-memcached.ini
                  echo 'extension=/usr/lib64/php/7.0/modules/amazon-elasticache-cluster-client.so;' >> /etc/php-7.0.d/50-memcached.ini
              mode: 000500
              owner: root
              group: root
        build_opcache:
          packages:
            yum:
              php70-opcache: []
          files:
            /tmp/install_opcache.sh:
              content: |
                  #!/bin/bash -xe
                  # create hidden opcache directory locally & change owner to apache
                  if [ ! -d /var/www/.opcache ]; then                    
                      mkdir -p /var/www/.opcache
                  fi
                  # enable opcache in /etc/php-7.0.d/10-opcache.ini
                  sed -i 's/;opcache.file_cache=.*/opcache.file_cache=\/var\/www\/.opcache/' /etc/php-7.0.d/10-opcache.ini
                  sed -i 's/opcache.memory_consumption=.*/opcache.memory_consumption=512/' /etc/php-7.0.d/10-opcache.ini
                  # download opcache-instance.php to verify opcache status
                  if [ ! -f /var/www/wordpress/wordpress/opcache-instanceid.php ]; then
                      wget -P /var/www/wordpress/wordpress/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/opcache-instanceid.php
                  fi
              mode: 000500
              owner: root
              group: root
        build_wordpress:
          files:
            /tmp/install_wordpress.sh:
              content:
                !Join [
                  "",[
                    "#!/bin/bash -xe\n",
                    "\n",
                    "# install wp-cli\n",
                    "if [ ! -f /bin/wp/wp-cli.phar ]; then\n",
                    "   curl -o /bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar\n",
                    "   chmod +x /bin/wp\n",
                    "fi\n",
                    "\n",
                    "# make site directory\n",
                    "if [ ! -d /var/www/wordpress/wordpress ]; then\n",                    
                    "   mkdir -p /var/www/wordpress/wordpress\n",
                    "\n",
                    "   cd /var/www/wordpress/wordpress\n",
                    "   # install wordpress if not installed\n",
                    "   # use public alb host name if wp domain name was empty\n",
                    "   if ! $(wp core is-installed --allow-root); then\n",
                    "       wp core download --version='latest' --locale='en_GB' --allow-root\n",
                    "       wp core config --dbname='wordpressdb' --dbuser='wordpress' --dbpass='supersecret' --dbhost='", !GetAtt DatabaseCluster.Endpoint.Address, "' --dbprefix=wp_ --allow-root\n",
                    "       wp core install --url='", !GetAtt ALB.DNSName, "' --title='Test' --admin_user='admin' --admin_password='instruqt' --admin_email='noreply@xebia.com' --skip-email --allow-root\n",
                    "       wp plugin install w3-total-cache\n",
                    "       sed -i \"/$table_prefix = 'wp_';/ a \\define('WP_HOME', 'http://' . \\$_SERVER['HTTP_HOST']); \" /var/www/wordpress/wordpress/wp-config.php\n",
                    "       sed -i \"/$table_prefix = 'wp_';/ a \\define('WP_SITEURL', 'http://' . \\$_SERVER['HTTP_HOST']); \" /var/www/wordpress/wordpress/wp-config.php\n",
                    "       # set permissions of wordpress site directories\n", 
                    "       chown -R apache:apache /var/www/wordpress/wordpress\n",
                    "       chmod u+wrx /var/www/wordpress/wordpress/wp-content/*\n",
                    "       if [ ! -f /var/www/wordpress/wordpress/opcache-instanceid.php ]; then\n",
                    "         wget -P /var/www/wordpress/wordpress/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/opcache-instanceid.php\n",
                    "       fi\n",
                    "   fi\n",
                    "   RESULT=$?\n",
                    "   if [ $RESULT -eq 0 ]; then\n",
                    "       touch /var/www/wordpress/wordpress/wordpress.initialized\n",
                    "         else\n",
                    "       touch /var/www/wordpress/wordpress/wordpress.failed\n",
                    "   fi\n",
                    "fi\n"
                  ]
                ]
              mode: 000500
              owner: root
              group: root
        download_aws_ini: 
          files:
            /tmp/download_aws_ini.sh:
              content:
                !Join [
                  "",[
                    "#!/bin/bash -x\n",
                    "\n",
                    "wget -P /etc/php-7.0.d/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/20-aws.ini\n"
                  ]
                ]
              mode: 000500
              owner: root
              group: root
        install_aws_ini:
          commands:
            install_aws_ini:
              command: ./download_aws_ini.sh
              cwd: /tmp
              ignoreErrors: true
        install_wordpress:
          commands:
            install_wordpress:
              command: ./install_wordpress.sh
              cwd: /tmp
              ignoreErrors: false          
        install_cacheclient:
          commands:
            install_cacheclient:
              command: ./install_cacheclient.sh
              cwd: /tmp
              ignoreErrors: false
        install_opcache:
          commands:
            install_opcache:
              command: ./install_opcache.sh
              cwd: /tmp
              ignoreErrors: false
        start_webserver:
          services:
            sysvinit:
              httpd:
                enabled: true
                ensureRunning: true

  # 15 auto scaling group

  WebAutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    DependsOn:
      - EFSMountTargetA
      - EFSMountTargetB
      - NAT
    Properties:
      Cooldown: "60"
      HealthCheckGracePeriod: 120
      HealthCheckType: ELB
      LaunchConfigurationName: !Ref WebLaunchConfiguration
      MaxSize: "2"
      MinSize: "1"
      TargetGroupARNs:
        - !Ref ALBTargetGroup
      VPCZoneIdentifier: 
        - !Ref PrivateSubnetA
        - !Ref PrivateSubnetB
    CreationPolicy:
      ResourceSignal:
        Count: 1
        Timeout: PT15M
Resources:
  # 01 Create VPC
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: "10.0.0.0/16"
      EnableDnsHostnames: true
  InternetGateway:
    Type: AWS::EC2::InternetGateway
  GatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  # 2 Add Route Table
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
      RouteTableId: !Ref PublicRouteTable

  ## 3 Add Public Subnets
  PublicSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]]
      CidrBlock: "10.0.0.0/24"
  PublicSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetA
      RouteTableId: !Ref PublicRouteTable
  PublicSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]]
      CidrBlock: "10.0.1.0/24"
  PublicSubnetBRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetB
      RouteTableId: !Ref PublicRouteTable

  # 4 Add NAT
  EIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
  NAT:
    DependsOn: GatewayAttachment
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !Sub "${EIP.AllocationId}"
      SubnetId: !Ref PublicSubnetA

  # 5 Add private Route Table
  PrivateSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: VPC
  PrivateRouteTableRouteNAT:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateSubnetRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NAT

  # 6 Add private subnets and associate route tables
  PrivateSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]]
      CidrBlock: "10.0.64.0/24"
  PrivateSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnetA
      RouteTableId: !Ref PrivateSubnetRouteTable
  PrivateSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]]
      CidrBlock: "10.0.65.0/24"
  PrivateSubnetBRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnetB
      RouteTableId: !Ref PrivateSubnetRouteTable

  # 7 add data route without any rules
  DataSubnetRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC

  DataSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]]
      CidrBlock: "10.0.128.0/24"
  DataSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref DataSubnetA
      RouteTableId: !Ref DataSubnetRouteTable
  DataSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]]
      CidrBlock: "10.0.129.0/24"
  DataSubnetBRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref DataSubnetB
      RouteTableId: !Ref DataSubnetRouteTable

  # 8 Security Groups

  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for ALB"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      VpcId: !Ref VPC
  WebSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for Web Servers"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref ALBSecurityGroup
      VpcId: !Ref VPC
  EFSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for EFS"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 2049
          ToPort: 2049
          SourceSecurityGroupId: !Ref WebSecurityGroup
      VpcId: !Ref VPC
  ElasticacheSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for ElastiCache"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 11211
          ToPort: 11211
          SourceSecurityGroupId: !Ref WebSecurityGroup
      VpcId: !Ref VPC
  DatabaseSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: "Security group for Amazon RDS cluster"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !Ref WebSecurityGroup
      VpcId: !Ref VPC


  # 9 ALB
  ALB: 
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      Subnets: 
        - !Ref PublicSubnetA
        - !Ref PublicSubnetB
      LoadBalancerAttributes:
      - Key: idle_timeout.timeout_seconds
        Value: "60"
      SecurityGroups:
      - !Ref ALBSecurityGroup
  ALBListener:
    Type : AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
      - Type: forward
        TargetGroupArn: !Ref ALBTargetGroup
      LoadBalancerArn: !Ref ALB
      Port: 80
      Protocol: HTTP
  ALBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 30
      HealthCheckPath: /wp-login.php
      
      HealthCheckTimeoutSeconds: 5
      Port: 80
      Protocol: HTTP
      UnhealthyThresholdCount: 5
      VpcId: !Ref VPC

  # 10 Add ElastiCache

  ElastiCacheSubnetGroup: 
    Type: AWS::ElastiCache::SubnetGroup
    Properties: 
      Description: ElastiCache Subnet Group for WordPress
      SubnetIds:
        - !Ref DataSubnetA
        - !Ref DataSubnetB
  ElastiCacheCluster:
    Type: AWS::ElastiCache::CacheCluster
    Properties:
      CacheNodeType: 'cache.t2.micro'
      CacheSubnetGroupName: !Ref ElastiCacheSubnetGroup
      Engine: memcached
      NumCacheNodes: 1
      VpcSecurityGroupIds:
      - !Ref ElasticacheSecurityGroup
  
  # 11 Database Cluster

  DataSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: "RDS Database Subnet Group for WordPress"
      SubnetIds: 
        - !Ref DataSubnetA
        - !Ref DataSubnetB
  DatabaseCluster:
    Type: AWS::RDS::DBCluster
    Properties:
      MasterUsername: "wordpress"
      MasterUserPassword: "supersecret"
      DatabaseName: "wordpressdb"
      Engine: aurora
      Port: 3306
      EngineMode: serverless
      ScalingConfiguration:
        AutoPause: false
        MaxCapacity: 16
        MinCapacity: 2
      DBSubnetGroupName: !Ref DataSubnetGroup
      VpcSecurityGroupIds:
      - !Ref DatabaseSecurityGroup

  # 12 EFS

  EFS:
    Type: AWS::EFS::FileSystem
    Properties:
      PerformanceMode: 'maxIO'
  EFSMountTargetA:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: !Ref EFS
      SecurityGroups:
        - !Ref EFSSecurityGroup
      SubnetId: !Ref DataSubnetA
  EFSMountTargetB:
    Type: AWS::EFS::MountTarget
    Properties:
      FileSystemId: !Ref EFS
      SecurityGroups:
        - !Ref EFSSecurityGroup
      SubnetId: !Ref DataSubnetB

  # 13 Instance Profile

  WebInstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
        - Effect: Allow
          Principal:
            Service: ec2.amazonaws.com
          Action: sts:AssumeRole
      Policies:
      - PolicyName: logs
        PolicyDocument:
          Version: 2012-10-17
          Statement:
          - Effect: Allow
            Action:
            - logs:CreateLogGroup
            - logs:CreateLogStream
            - logs:PutLogEvents
            - logs:DescribeLogStreams
            Resource:
            - arn:aws:logs:*:*:*
  WebInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
      - !Ref WebInstanceRole

  ## 14 AutoScaling

  WebLaunchConfiguration:
    Type: AWS::AutoScaling::LaunchConfiguration
    Properties:
      IamInstanceProfile: !Ref WebInstanceProfile
      ImageId: "ami-1a962263"
      InstanceMonitoring: true
      InstanceType: "t3.xlarge"
      SecurityGroups:
      - !Ref WebSecurityGroup
      UserData:
        "Fn::Base64":
          !Sub |
            #!/bin/bash -xe
            yum update -y
            mkdir -p /var/www/wordpress
            mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 ${EFS}.efs.${AWS::Region}.amazonaws.com:/ /var/www/wordpress
            /opt/aws/bin/cfn-init --configsets deploy_webserver --verbose --stack ${AWS::StackName} --resource WebLaunchConfiguration --region ${AWS::Region}
            /opt/aws/bin/cfn-signal --exit-code $? --stack ${AWS::StackName} --resource WebAutoScalingGroup --region ${AWS::Region}
    Metadata:
      AWS::CloudFormation::Init:
        configSets:
          deploy_webserver:
            - install_webserver
            - build_cacheclient
            - build_wordpress
            - build_opcache
            - download_aws_ini
            - install_aws_ini
            - install_cacheclient
            - install_wordpress
            - install_opcache
            - start_webserver
        install_webserver:
          packages:
            yum:
              awslogs: []
              httpd24: []
              mysql56: []
              php70: []
              php70-devel: []
              php7-pear: []
              php70-mysqlnd: []
          files:
            /tmp/create_site_conf.sh:
              content:
                !Join [
                  "",[
                    "#!/bin/bash -xe\n",
                    "if [ ! -f /etc/httpd/conf.d/wordpress.conf ]; then\n",                    
                    "   touch /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo 'ServerName 127.0.0.1:80' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo 'DocumentRoot /var/www/wordpress/wordpress' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo '<Directory /var/www/wordpress/wordpress>' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo '  Options Indexes FollowSymLinks' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo '  AllowOverride All' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo '  Require all granted' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "   echo '</Directory>' >> /etc/httpd/conf.d/wordpress.conf\n",
                    "fi\n"
                  ]
                ]
              mode: 000500
              owner: root
              group: root
          commands:
            create_site_conf:
              command: ./create_site_conf.sh
              cwd: /tmp
              ignoreErrors: false
        build_cacheclient:
          packages:
            yum:
              gcc-c++: []  
          files:
            /tmp/install_cacheclient.sh:
              content: |
                  #!/bin/bash -xe
                  ln -s /usr/bin/pecl7 /usr/bin/pecl #just so pecl is available easily
                  pecl7 install igbinary
                  wget -P /tmp/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/AmazonElastiCacheClusterClient-2.0.1-PHP70-64bit.tar.gz
                  tar -xf '/tmp/AmazonElastiCacheClusterClient-2.0.1-PHP70-64bit.tar.gz'
                  cp '/tmp/artifact/amazon-elasticache-cluster-client.so' /usr/lib64/php/7.0/modules/
                  if [ ! -f /etc/php-7.0.d/50-memcached.ini ]; then
                      touch /etc/php-7.0.d/50-memcached.ini
                  fi
                  echo 'extension=igbinary.so;' >> /etc/php-7.0.d/50-memcached.ini
                  echo 'extension=/usr/lib64/php/7.0/modules/amazon-elasticache-cluster-client.so;' >> /etc/php-7.0.d/50-memcached.ini
              mode: 000500
              owner: root
              group: root
        build_opcache:
          packages:
            yum:
              php70-opcache: []
          files:
            /tmp/install_opcache.sh:
              content: |
                  #!/bin/bash -xe
                  # create hidden opcache directory locally & change owner to apache
                  if [ ! -d /var/www/.opcache ]; then                    
                      mkdir -p /var/www/.opcache
                  fi
                  # enable opcache in /etc/php-7.0.d/10-opcache.ini
                  sed -i 's/;opcache.file_cache=.*/opcache.file_cache=\/var\/www\/.opcache/' /etc/php-7.0.d/10-opcache.ini
                  sed -i 's/opcache.memory_consumption=.*/opcache.memory_consumption=512/' /etc/php-7.0.d/10-opcache.ini
                  # download opcache-instance.php to verify opcache status
                  if [ ! -f /var/www/wordpress/wordpress/opcache-instanceid.php ]; then
                      wget -P /var/www/wordpress/wordpress/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/opcache-instanceid.php
                  fi
              mode: 000500
              owner: root
              group: root
        build_wordpress:
          files:
            /tmp/install_wordpress.sh:
              content:
                !Join [
                  "",[
                    "#!/bin/bash -xe\n",
                    "\n",
                    "# install wp-cli\n",
                    "if [ ! -f /bin/wp/wp-cli.phar ]; then\n",
                    "   curl -o /bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar\n",
                    "   chmod +x /bin/wp\n",
                    "fi\n",
                    "\n",
                    "# make site directory\n",
                    "if [ ! -d /var/www/wordpress/wordpress ]; then\n",                    
                    "   mkdir -p /var/www/wordpress/wordpress\n",
                    "\n",
                    "   cd /var/www/wordpress/wordpress\n",
                    "   # install wordpress if not installed\n",
                    "   # use public alb host name if wp domain name was empty\n",
                    "   if ! $(wp core is-installed --allow-root); then\n",
                    "       wp core download --version='latest' --locale='en_GB' --allow-root\n",
                    "       wp core config --dbname='wordpressdb' --dbuser='wordpress' --dbpass='supersecret' --dbhost='", !GetAtt DatabaseCluster.Endpoint.Address, "' --dbprefix=wp_ --allow-root\n",
                    "       wp core install --url='", !GetAtt ALB.DNSName, "' --title='Test' --admin_user='admin' --admin_password='instruqt' --admin_email='noreply@xebia.com' --skip-email --allow-root\n",
                    "       wp plugin install w3-total-cache\n",
                    "       sed -i \"/$table_prefix = 'wp_';/ a \\define('WP_HOME', 'http://' . \\$_SERVER['HTTP_HOST']); \" /var/www/wordpress/wordpress/wp-config.php\n",
                    "       sed -i \"/$table_prefix = 'wp_';/ a \\define('WP_SITEURL', 'http://' . \\$_SERVER['HTTP_HOST']); \" /var/www/wordpress/wordpress/wp-config.php\n",
                    "       # set permissions of wordpress site directories\n", 
                    "       chown -R apache:apache /var/www/wordpress/wordpress\n",
                    "       chmod u+wrx /var/www/wordpress/wordpress/wp-content/*\n",
                    "       if [ ! -f /var/www/wordpress/wordpress/opcache-instanceid.php ]; then\n",
                    "         wget -P /var/www/wordpress/wordpress/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/opcache-instanceid.php\n",
                    "       fi\n",
                    "   fi\n",
                    "   RESULT=$?\n",
                    "   if [ $RESULT -eq 0 ]; then\n",
                    "       touch /var/www/wordpress/wordpress/wordpress.initialized\n",
                    "         else\n",
                    "       touch /var/www/wordpress/wordpress/wordpress.failed\n",
                    "   fi\n",
                    "fi\n"
                  ]
                ]
              mode: 000500
              owner: root
              group: root
        download_aws_ini: 
          files:
            /tmp/download_aws_ini.sh:
              content:
                !Join [
                  "",[
                    "#!/bin/bash -x\n",
                    "\n",
                    "wget -P /etc/php-7.0.d/ https://s3.amazonaws.com/aws-refarch/wordpress/latest/bits/20-aws.ini\n"
                  ]
                ]
              mode: 000500
              owner: root
              group: root
        install_aws_ini:
          commands:
            install_aws_ini:
              command: ./download_aws_ini.sh
              cwd: /tmp
              ignoreErrors: true
        install_wordpress:
          commands:
            install_wordpress:
              command: ./install_wordpress.sh
              cwd: /tmp
              ignoreErrors: false          
        install_cacheclient:
          commands:
            install_cacheclient:
              command: ./install_cacheclient.sh
              cwd: /tmp
              ignoreErrors: false
        install_opcache:
          commands:
            install_opcache:
              command: ./install_opcache.sh
              cwd: /tmp
              ignoreErrors: false
        start_webserver:
          services:
            sysvinit:
              httpd:
                enabled: true
                ensureRunning: true

  # 15 auto scaling group

  WebAutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    DependsOn:
      - EFSMountTargetA
      - EFSMountTargetB
      - NAT
    Properties:
      Cooldown: "60"
      HealthCheckGracePeriod: 120
      HealthCheckType: ELB
      LaunchConfigurationName: !Ref WebLaunchConfiguration
      MaxSize: "2"
      MinSize: "1"
      TargetGroupARNs:
        - !Ref ALBTargetGroup
      VPCZoneIdentifier: 
        - !Ref PrivateSubnetA
        - !Ref PrivateSubnetB
    CreationPolicy:
      ResourceSignal:
        Count: 1
        Timeout: PT15M