

# CLI Tutorial: High Availability Two-Tier Stack (Linux/RHEL)
<a name="tut-create-ha-stack"></a>

This section describes how to deploy a high availability (HA) two-tier stack into an AMS environment using the AMS CLI. 

**Note**  
This deployment walkthrough has been tested in AMZN Linux and RHEL environments.

Summary of tasks and required RFCs:

1. Create infrastructure (HA two-tier stack)

1. Create an S3 bucket for CodeDeploy applications

1. Create the WordPress application bundle and upload it to the S3 bucket

1. Deploy the application with CodeDeploy

1. Access the WordPress site and log in to validate the deployment

# Before You Begin
<a name="ha-stack-ex-before-begin"></a>

The Deployment \$1 Advanced Stack Components \$1 High Availability Two Tier Stack Advanced \$1 Create CT creates an Auto Scaling group, a load balancer, a database, and a CodeDeploy application name and deployment group (with the same name that you give the application). For information on CodeDeploy see [What is CodeDeploy?](https://docs.aws.amazon.com/codedeploy/latest/userguide/welcome.html)

This walkthrough uses a High Availability Two-Tier Stack (Advanced) RFC that includes UserData and also describes how to create a WordPress bundle that CodeDeploy can deploy.

The `UserData` shown in the example gets instance metadata such as instance ID, region, etc, from within a running instance by querying the EC2 instance metadata service available at http://169.254.169.254/latest/meta-data/. This line in the user data script: `REGION=$(curl 169.254.169.254/latest/meta-data/placement/availability-zone/ | sed 's/[a-z]$//')`, retrieves the availability zone name from the meta-data service into the \$1REGION variable for our supported regions, and uses it to complete the URL for the S3 bucket where the CodeDeploy agent is downloaded. The 169.254.169.254 IP is routable only within the VPC (all VPCs can query the service). For information about the service, see [ Instance Metadata and User Data](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html). Note also that scripts entered as UserData are executed as the "root" user and do not need to use the "sudo" command.

This walkthrough leaves the following parameters at the default value (shown):
+ Auto Scaling group: `Cooldown=300, DesiredCapacity=2, EBSOptimized=false, HealthCheckGracePeriod=600, IAMInstanceProfile=customer-mc-ec2-instance-profile, InstanceDetailedMonitoring=true, InstanceRootVolumeIops=0, InstanceRootVolumeType=standard, InstanceType=m3.medium, MaxInstances=2, MinInstances=2, ScaleDownPolicyCooldown=300, ScaleDownPolicyEvaluationPeriods=4, ScaleDownPolicyPeriod=60, ScaleDownPolicyScalingAdjustment=-1, ScaleDownPolicyStatistic=Average, ScaleDownPolicyThreshold=35, ScaleMetricName=CPUUtilization, ScaleUpPolicyCooldown=60, ScaleUpPolicyEvaluationPeriods=2, ScaleUpPolicyPeriod=60, ScaleUpPolicyScalingAdjustment=2, ScaleUpPolicyStatistic=Average, ScaleUpPolicyThreshold=75`.
+ Load Balancer: `HealthCheckInterval=30, HealthCheckTimeout=5`.
+ Database: `BackupRetentionPeriod=7, Backups=true, InstanceType=db.m3.medium, IOPS=0, MultiAZ=true, PreferredBackupWindow=22:00-23:00, PreferredMaintenanceWindow=wed:03:32-wed:04:02, StorageEncrypted=false, StorageEncryptionKey="", StorageType=gp2`.
+ Application: `DeploymentConfigName=CodeDeployDefault.OneAtATime`.
+ S3 bucket: `AccessControl=Private`.

ADDITIONAL SETTINGS:

`RequestedStartTime` and `RequestedEndTime` if you want to schedule your RFC: You can use [Time.is](https://time.is/UTC) to determine the correct UTC time. The examples provided must be adjusted appropriately. An RFC cannot proceed if the start time has passed. Alternatively, you can leave those values off to create an ASAP RFC that executes as soon as approvals are passed.

**Note**  
There are many parameters that you might choose to set differently than as shown. The values for those parameters shown in the example have been tested but may not be right for you.

# Create the Infrastructure
<a name="ex-create-ha-infra-deploy"></a>

Gathering the following data before you begin will make the deployment go more quickly.

REQUIRED DATA HA STACK:
+ AutoScalingGroup:
  + `UserData`: This value is provided in this tutorial. It includes commands to set up the resource for CodeDeploy and start the CodeDeploy agent.
  + `AMI-ID`: This value determines what kind of EC2 instances your Auto Scaling group (ASG) will spin up. Be sure to select an AMI in your account that starts with "customer-" and is of the operating system that you want. Find AMI IDs with the For the AMS SKMS API reference, see the **Reports** tab in the AWS Artifact Console. operation (CLI: list-amis) or in the AMS Console VPCs -> VPCs details page. This walkthrough is for ASGs configured to use a Linux AMI.
+ Database:
  + These parameters, `DBEngine`, `EngineVersion`, and `LicenseModel` should be set according to your situation though the values shown in the example have been tested.
  + These parameters, `RDSSubnetIds`, `DBName`, `MasterUsername`, and `MasterUserPassword` are required when deploying the application bundle. For RDSSubnetIds, use two Private subnets.
+ LoadBalancer:
  + These parameters, `DBEngine`, `EngineVersion`, and `LicenseModel` should be set according to your situation though the values shown in the example have been tested.
  + `ELBSubnetIds`: Use two Public subnets.
+ Application: The `ApplicationName` value sets the CodeDeploy application name and CodeDeploy deployment group name. You use it to deploy your application. It must be unique in the account. To check your account for CodeDeploy names, see the CodeDeploy Console. The example uses "WordPress" but, if you will use that value, make sure that it is not already in use.

This procedure utilizes the High availability two-tier stack (advanced) CT (ct-06mjngx5flwto) and the Create S3 storage CT (ct-1a68ck03fn98r). From your authenticated account, follow these steps at the command line.

1. Launch the infrastructure stack.

   1. Output the execution parameters JSON schema for the HA two tier stack CT to a file in your current folder named CreateStackParams.json.

      ```
      aws amscm get-change-type-version --change-type-id "ct-06mjngx5flwto" --query "ChangeTypeVersion.ExecutionInputSchema" --output text > CreateStackParams.json
      ```

   1. Modify the schema. Replace the *variables* as appropriate. For example, use the OS that you want for the EC2 instances the ASG will create. Record the `ApplicationName` as you will use it later to deploy the application. Note that you can add up to 50 tags.

      ```
      {
      "Description":      "HA two tier stack for WordPress",
      "Name":             "WordPressStack",
      "TimeoutInMinutes":  360,
      "Tags": [
              {
                  "Key": "ApplicationName",
                  "Value": "WordPress"
              }
          ],
      "AutoScalingGroup": {
                  "AmiId":    "AMI-ID",
                  "UserData": "#!/bin/bash \n
                  REGION=$(curl 169.254.169.254/latest/meta-data/placement/availability-zone/ | sed 's/[a-z]$//') \n
                  yum -y install ruby httpd \n
                  chkconfig httpd on \n
                  service httpd start \n
                  touch /var/www/html/status \n
                  cd /tmp \n
                  curl -O https://aws-codedeploy-$REGION.s3.amazonaws.com/latest/install \n
                  chmod +x ./install \n
                  ./install auto \n
                  chkconfig codedeploy-agent on \n
                  service codedeploy-agent start"
          },
          "LoadBalancer": {
              "Public":               true,
              "HealthCheckTarget":    "HTTP:80/status"
          },
          "Database":     {
              "DBEngine":             "MySQL",
              "DBName":               "wordpress",
              "EngineVersion":        "8.0.16 ",
              "LicenseModel":         "general-public-license",
              "MasterUsername":       "admin",
              "MasterUserPassword":   "p4ssw0rd"
          },
          "Application":  {
          "ApplicationName":  "WordPress"
              }
      }
      ```

   1. Output the CreateRfc JSON template to a file in your current folder named CreateStackRfc.json:

      ```
      aws amscm create-rfc --generate-cli-skeleton > CreateStackRfc.json
      ```

   1. Modify the RFC template as follows and save it, you can delete and replace the contents. Note that `RequestedStartTime` and `RequestedEndTime` are now optional; excluding them creates an ASAP RFC that executes as soon as it is approved (which usually happens automatically). To submit a scheduled RFC, add those values.

      ```
      {
      "ChangeTypeVersion":    "3.0",
      "ChangeTypeId":         "ct-06mjngx5flwto",
      "Title":                "HA-Stack-For-WP-RFC"
      }
      ```

   1. Create the RFC, specifying the CreateStackRfc.json file and the CreateStackParams.json execution parameters file:

      ```
      aws amscm create-rfc --cli-input-json file://CreateStackRfc.json --execution-parameters file://CreateStackParams.json
      ```

      You receive the RFC ID in the response. Save the ID for subsequent steps.

   1. Submit the RFC:

      ```
      aws amscm submit-rfc --rfc-id  RFC_ID
      ```

      If the RFC succeeds, you receive no output.

   1. To check RFC status, run 

      ```
      aws amscm get-rfc --rfc-id RFC_ID
      ```

   Keep note of the RFC ID.

1. Launch an S3 bucket

   Gathering the following data before you begin will make the deployment go more quickly.

   REQUIRED DATA S3 BUCKET:
   + `VPC-ID`: This value determines where your S3 Bucket will be. Use the same VPC ID that you used previously.
   + `BucketName`: This value sets the S3 Bucket name, you use it to upload your application bundle. It must be unique across the region of the account and cannot include upper-case letters. Including your account ID as part of the BucketName is not a requirement but makes it easier to identify the bucket later. To see what S3 bucket names exist in the account, go to the Amazon S3 Console for your account.

   1. Output the execution parameters JSON schema for the S3 storage create CT to a JSON file named CreateS3StoreParams.json.

      ```
      aws amscm get-change-type-version --change-type-id "ct-1a68ck03fn98r" --query "ChangeTypeVersion.ExecutionInputSchema" --output text > CreateS3StoreParams.json
      ```

   1. Modify the schema as follows, you can delete and replace the contents. Replace *VPC\$1ID* appropriately. The values in the example have been tested, but may not be right for you.
**Tip**  
The `BucketName` must be unique across the region of the account and cannot include upper-case letters. Including your account ID as part of the BucketName is not a requirement but makes it easier to identify the bucket later. To see what S3 bucket names exist in the account, go to the Amazon S3 Console for your account.

      ```
      {
      "Description":      "S3BucketForWordPressBundle",
      "VpcId":            "VPC_ID",
      "StackTemplateId":  "stm-s2b72beb000000000",
      "Name":             "S3BucketForWP",
      "TimeoutInMinutes":  60,
      "Parameters":   {
          "AccessControl":    "Private",
          "BucketName":       "ACCOUNT_ID-BUCKET_NAME"
          }
      }
      ```

   1. Output the JSON template for CreateRfc to a file, in your current folder, named CreateS3StoreRfc.json:

      ```
      aws amscm create-rfc --generate-cli-skeleton > CreateS3StoreRfc.json
      ```

   1. Modify and save the CreateS3StoreRfc.json file, you can delete and replace the contents. Note that `RequestedStartTime` and `RequestedEndTime` are now optional; excluding them creates an ASAP RFC that executes as soon as it is approved (which usually happens automatically). To submit a scheduled RFC, add those values.

      ```
      {
      "ChangeTypeVersion":    "1.0",
      "ChangeTypeId":         "ct-1a68ck03fn98r",
      "Title":                "S3-Stack-For-WP-RFC"
      }
      ```

   1. Create the RFC, specifying the CreateS3StoreRfc.json file and the CreateS3StoreParams.json execution parameters file:

      ```
      aws amscm create-rfc --cli-input-json file://CreateS3StoreRfc.json  --execution-parameters file://CreateS3StoreParams.json
      ```

      You receive the RfcId of the new RFC in the response. Save the ID for subsequent steps.

   1. Submit the RFC:

      ```
      aws amscm submit-rfc --rfc-id RFC_ID
      ```

      If the RFC succeeds, you receive no output.

   1. To check RFC status, run 

      ```
      aws amscm get-rfc --rfc-id RFC_ID
      ```

# Create, Upload, and Deploy the Application
<a name="ex-create-app"></a>

First, create a WordPress application bundle, and then use the CodeDeploy CTs to create and deploy the application.

1. Download WordPress, extract the files and create a ./scripts directory.

   Linux command:

   ```
   wget https://github.com/WordPress/WordPress/archive/master.zip
   ```

   Windows: Paste `https://github.com/WordPress/WordPress/archive/master.zip` into a browser window and download the zip file.

   Create a temporary directory in which to assemble the package.

   Linux:

   ```
   mkdir /tmp/WordPress
   ```

   Windows: Create a "WordPress" directory, you will use the directory path later.

1. Extract the WordPress source to the "WordPress" directory and create a ./scripts directory.

   Linux:

   ```
   unzip master.zip -d /tmp/WordPress_Temp
   cp -paf /tmp/WordPress_Temp/WordPress-master/* /tmp/WordPress
   rm -rf /tmp/WordPress_Temp
   rm -f master
   cd /tmp/WordPress
   mkdir scripts
   ```

   Windows: Go to the "WordPress" directory that you created and create a "scripts" directory there.

   If you are in a Windows environment, be sure to set the break type for the script files to Unix (LF). In Notepad \$1\$1, this is an option at the bottom right of the window.

1. Create the CodeDeploy **appspec.yml** file, in the WordPress directory (if copying the example, check the indentation, each space counts). IMPORTANT: Ensure that the "source" path is correct for copying the WordPress files (in this case, in your WordPress directory) to the expected destination (/var/www/html/WordPress). In the example, the appspec.yml file is in the directory with the WordPress files, so only "/" is needed. Also, even if you used a RHEL AMI for your Auto Scaling group, leave the "os: linux" line as-is. Example appspec.yml file:

   ```
   version: 0.0
   os: linux
   files:
     - source: /
       destination: /var/www/html/WordPress
   hooks:
     BeforeInstall:
       - location: scripts/install_dependencies.sh
         timeout: 300
         runas: root
     AfterInstall:
       - location: scripts/config_wordpress.sh
         timeout: 300
         runas: root
     ApplicationStart:
       - location: scripts/start_server.sh
         timeout: 300
         runas: root
     ApplicationStop:
       - location: scripts/stop_server.sh
         timeout: 300
         runas: root
   ```

1. Create bash file scripts in the WordPress ./scripts directory.

   First, create `config_wordpress.sh` with the following content (if you prefer, you can edit the wp-config.php file directly).
**Note**  
Replace *DBName* with the value given in the HA Stack RFC (for example, `wordpress`).  
Replace *DB\$1MasterUsername* with the `MasterUsername` value given in the HA Stack RFC (for example, `admin`).  
Replace *DB\$1MasterUserPassword* with the `MasterUserPassword` value given in the HA Stack RFC (for example, `p4ssw0rd`).  
Replace *DB\$1ENDPOINT* with the endpoint DNS name in the execution outputs of the HA Stack RFC (for example, `srt1cz23n45sfg.clgvd67uvydk.us-east-1.rds.amazonaws.com`). You can find this with the [GetRfc](https://docs.aws.amazon.com/managedservices/latest/ApiReference-cm/API_GetRfc.html) operation (CLI: get-rfc --rfc-id RFC\$1ID) or in the AMS Console RFC details page for the HA Stack RFC that you previously submitted.

   ```
   #!/bin/bash
   chmod -R 755 /var/www/html/WordPress
   cp /var/www/html/WordPress/wp-config-sample.php /var/www/html/WordPress/wp-config.php
   cd /var/www/html/WordPress
   sed -i "s/database_name_here/DBName/g" wp-config.php
   sed -i "s/username_here/DB_MasterUsername/g" wp-config.php
   sed -i "s/password_here/DB_MasterUserPassword/g" wp-config.php
   sed -i "s/localhost/DB_ENDPOINT/g" wp-config.php
   ```

1. In the same directory create `install_dependencies.sh` with the following content:

   ```
   #!/bin/bash
   yum install -y php
   yum install -y php-mysql
   yum install -y mysql
   service httpd restart
   ```
**Note**  
HTTPS is installed as part of the user data at launch in order to allow health checks to work from the start.

1. In the same directory create `start_server.sh` with the following content:
   + For Amazon Linux instances, use this:

     ```
     #!/bin/bash
     service httpd start
     ```
   + For RHEL instances, use this (the extra commands are policies that allow SELINUX to accept WordPress):

     ```
     #!/bin/bash
     setsebool -P  httpd_can_network_connect_db 1
     setsebool -P  httpd_can_network_connect 1
     chcon -t httpd_sys_rw_content_t /var/www/html/WordPress/wp-content -R
     restorecon -Rv /var/www/html
     service httpd start
     ```

1. In the same directory create `stop_server.sh` with the following content:

   ```
   #!/bin/bash
   service httpd stop
   ```

1. Create the zip bundle.

   Linux:

   ```
   $ cd /tmp/WordPress
   $ zip -r wordpress.zip .
   ```

   Windows: Go to your "WordPress" directory and select all of the files and create a zip file, be sure to name it wordpress.zip.

1. Upload the application bundle to the S3 bucket.

   The bundle needs to be in place in order to continue deploying the stack. 

   You automatically have access to any S3 bucket instance that you create. You can access it through your bastions, or through the S3 console, and upload the WordPress bundle with drag-and-drop or browsing to and selecting the zip file.

   You can also use the following command in a shell window; be sure that you have the correct path to the zip file:

   ```
   aws s3 cp wordpress.zip s3://BUCKET_NAME/
   ```

1. Deploy the WordPress application bundle.

   Gathering the following data before you begin will make the deployment go more quickly.

   REQUIRED DATA:
   + `VPC-ID`: This value determines where your S3 Bucket will be. Use the same VPC ID that you used previously.
   + `CodeDeployApplicationName` and `CodeDeployApplicationName`: The `ApplicationName` value you used in the HA 2-Tier Stack RFC set the CodeDeployApplicationName and the CodeDeployDeploymentGroupName. The example uses "WordPress" but you may have used a different value.
   + `S3Location`: For `S3Bucket`, use the `BucketName` that you previously created. The `S3BundleType` and `S3Key` are from the bundle that you put on your S3 store.

   1. Output the execution parameters JSON schema for the CodeDeploy application deploy CT to a JSON file named DeployCDAppParams.json.

      ```
      aws amscm get-change-type-version --change-type-id "ct-2edc3sd1sqmrb" --query "ChangeTypeVersion.ExecutionInputSchema" --output text > DeployCDAppParams.json
      ```

   1. Modify the schema as follows and save it as, you can delete and replace the contents.

      ```
      {
      "Description":                       "DeployWPCDApp",
      "VpcId":                             "VPC_ID",
      "Name":                              "WordPressCDAppDeploy",
      "TimeoutInMinutes":                  60,
      "Parameters":   {
          "CodeDeployApplicationName":                "WordPress",
          "CodeDeployDeploymentGroupName":            "WordPress",
          "CodeDeployIgnoreApplicationStopFailures":   false,
          "CodeDeployRevision": {
            "RevisionType": "S3",
            "S3Location": {
              "S3Bucket":     "BUCKET_NAME",
              "S3BundleType": "zip",
              "S3Key":        "wordpress.zip" }
              }
          }
      }
      ```

   1. Output the JSON template for CreateRfc to a file, in your current folder, named DeployCDAppRfc.json:

      ```
      aws amscm create-rfc --generate-cli-skeleton > DeployCDAppRfc.json
      ```

   1. Modify and save the DeployCDAppRfc.json file, you can delete and replace the contents. Note that `RequestedStartTime` and `RequestedEndTime` are now optional; excluding them creates an ASAP RFC that executes as soon as it is approved (which usually happens automatically). To submit a scheduled RFC, add those values.

      ```
      {
      "ChangeTypeVersion":    "1.0",
      "ChangeTypeId":         "ct-2edc3sd1sqmrb",
      "Title":                "CD-Deploy-For-WP-RFC"
      }
      ```

   1. Create the RFC, specifying the DeployCDAppRfc file and the DeployCDAppParams execution parameters file:

      ```
      aws amscm create-rfc --cli-input-json file://DeployCDAppRfc.json  --execution-parameters file://DeployCDAppParams.json
      ```

      You receive the RfcId of the new RFC in the response. Save the ID for subsequent steps.

   1. Submit the RFC:

      ```
      aws amscm submit-rfc --rfc-id RFC_ID
      ```

      If the RFC succeeds, you receive no output.

   1. To check RFC status, run

      ```
      aws amscm get-rfc --rfc-id RFC_ID
      ```

# Validate the Application Deployment
<a name="ex-validate-app-deploy"></a>

Navigate to the endpoint (ELB CName) of the previously-created load balancer, with the WordPress deployed path: /WordPress. For example:

```
http://stack-ID-FOR-ELB.us-east-1.elb.amazonaws.com/WordPress
```

# Tear Down the Application Deployment
<a name="ex-tear-down-app-deploy"></a>

Once you are finished with the tutorial, you will want to tear down the deployment so you are not charged for the resources.

The following is a generic stack delete operation. You'll want to submit it twice, once for the HA 2-Tier stack and once for the S3 bucket stack. As a final follow-through, submit a service request that all snapshots for the S3 bucket (include the S3 bucket stack ID in the service request) be deleted. They are automatically deleted after 10 days, but deleting them early saves a little bit of cost.

This walkthrough provides an example of using the AMS console to delete an S3 stack; this procedure applies to deleting any stack using the AMS console.
**Note**  
If deleting an S3 bucket, it must be emptied of objects first.

REQUIRED DATA:
+ `StackId`: The stack to use. You can find this by looking at the AMS Console **Stacks** page, available through a link in the left nav. Using the AMS SKMS API/CLI, run the For the AMS SKMS API reference, see the **Reports** tab in the AWS Artifact Console. operation (`list-stack-summaries` in the CLI).
+ The change type ID for this walkthrough is `ct-0q0bic0ywqk6c`, the version is "1.0", to find out the latest version, run this command:

  ```
  aws amscm list-change-type-version-summaries --filter Attribute=ChangeTypeId,Value=ct-0q0bic0ywqk6c
  ```

*INLINE CREATE*:
+ Issue the create RFC command with execution parameters provided inline (escape quotes when providing execution parameters inline). E

  ```
  aws amscm create-rfc --change-type-id "ct-0q0bic0ywqk6c" --change-type-version "1.0" --title "Delete My Stack" --execution-parameters "{\"StackId\":\"STACK_ID\"}"
  ```
+ Submit the RFC using the RFC ID returned in the create RFC operation. Until submitted, the RFC remains in the `Editing` state and is not acted on.

  ```
  aws amscm submit-rfc --rfc-id RFC_ID
  ```
+ Monitor the RFC status and view execution output:

  ```
  aws amscm get-rfc --rfc-id RFC_ID
  ```

*TEMPLATE CREATE*:

1. Output the RFC template to a file in your current folder; example names it DeleteStackRfc.json:

   ```
   aws amscm create-rfc --generate-cli-skeleton > DeleteStackRfc.json
   ```

1. Modify and save the DeleteStackRfc.json file. Since deleting a stack has only one execution parameter, the execution parameters can be in the DeleteStackRfc.json file itself (there is no need to create a separate JSON file with execution parameters).

   The internal quotation marks in the ExecutionParameters JSON extension must be escaped with a backslash (\$1). Example without start and end time:

   ```
   {
   "ChangeTypeVersion":    "1.0",
   "ChangeTypeId":         "ct-0q0bic0ywqk6c",
   "Title":                "Delete-My-Stack-RFC"
   "ExecutionParameters":  "{
           \"StackId\":\"STACK_ID\"}"
   }
   ```

1. Create the RFC:

   ```
   aws amscm create-rfc --cli-input-json file://DeleteStackRfc.json 
   ```

   You receive the RfcId of the new RFC in the response. For example:

   ```
   {
   "RfcId": "daaa1867-ffc5-1473-192a-842f6b326102"
   }
   ```

   Save the ID for subsequent steps.

1. Submit the RFC:

   ```
   aws amscm submit-rfc --rfc-id RFC_ID
   ```

   If the RFC succeeds, you receive no confirmation at the command line.

1. To monitor the status of the request and to view Execution Output:

   ```
   aws amscm get-rfc --rfc-id RFC_ID --query "Rfc.{Status:Status.Name,Exec:ExecutionOutput}" --output table
   ```