Skip to content

Instantly share code, notes, and snippets.

@perfettiful
Forked from rmiyazaki6499/deploy-django.md
Created January 6, 2021 00:43
Show Gist options
  • Select an option

  • Save perfettiful/fa9d2a6c0cc2a0c7b77ee3298b36fa15 to your computer and use it in GitHub Desktop.

Select an option

Save perfettiful/fa9d2a6c0cc2a0c7b77ee3298b36fa15 to your computer and use it in GitHub Desktop.

Revisions

  1. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 26, 2020. 1 changed file with 714 additions and 714 deletions.
    1,428 changes: 714 additions & 714 deletions deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -1,608 +1,608 @@
    # Deploying a Production ready Django app on AWS

    In this tutorial, I will be going over to how to deploy a Django app from start to finish using AWS and EC2. Recently, my partner [Tu](https://github.com/tuvo1106) and I launched our app Hygge Homes (a vacation home rental app for searching and booking vacation homes based off Airbnb) and we wanted to share with other developers some of the lessons we learned along the way.
    # Deploying a Production ready Django app on AWS

    Following this tutorial, you will have an application that has:
    In this tutorial, I will be going over to how to deploy a Django app from start to finish using AWS and EC2. Recently, my partner [Tu](https://github.com/tuvo1106) and I launched our app Hygge Homes (a vacation home rental app for searching and booking vacation homes based off Airbnb) and we wanted to share with other developers some of the lessons we learned along the way.

    Following this tutorial, you will have an application that has:

    - An AWS EC2 server configured to host your application
    - SSL-certification with Certbot
    - A custom domain name
    - Continuous deployment with Github Actions/SSM Agent

    We have provided a dummy repo to follow along with [here](https://github.com/rmiyazaki6499/django-app.git), but feel free to apply it to your own application as necessary.
    ## Table of Contents
    We have provided a dummy repo to follow along with [here](https://github.com/rmiyazaki6499/django-app.git), but feel free to apply it to your own application as necessary.


    ## Table of Contents
    - [Project Layout](#project-layout)
    - [Previewing the Project](#previewing-the-project)
    - [Previewing the Django-app project with Docker](#previewing-the-django-app-project-with-docker)
    - [Install Docker](#install-docker)
    - [Cleaning up the Container and Image](#cleaning-up-the-container-and-image)
    - [Install Docker](#install-docker)
    - [Cleaning up the Container and Image](#cleaning-up-the-container-and-image)
    - [Previewing the Django-app project Locally](#previewing-the-django-app-project-locally)
    - [Creating an AWS Account](#creating-an-aws-account)
    - [Creating an AWS EC2 Instance](#creating-an-aws-ec2-instance)
    - [EC2 Console](#ec2-console)
    - [AMI](#ami)
    - [Security Groups](#security-groups)
    - [Instance Details](#instance-details)
    - [Key Pairs](#key-pairs)
    - [Elastic IP](#elastic-ip)
    - [Connecting to your EC2 Instance](#connecting-to-your-ec2-instance)
    - [EC2 Console](#ec2-console)
    - [AMI](#ami)
    - [Security Groups](#security-groups)
    - [Instance Details](#instance-details)
    - [Key Pairs](#key-pairs)
    - [Elastic IP](#elastic-ip)
    - [Connecting to your EC2 Instance](#connecting-to-your-ec2-instance)
    - [EC2 Environment Setup](#ec2-environment-setup)
    - [Firewall Setup](#firewall-setup)
    - [Installing Dependencies](#installing-dependencies)
    - [Setting up the Project on the Remote Server](#setting-up-the-project-on-the-remote-server)
    - [Configuring Gunicorn](#configuring-gunicorn)
    - [Configuring NGINX](#configuring-nginx)
    - [Firewall Setup](#firewall-setup)
    - [Installing Dependencies](#installing-dependencies)
    - [Setting up the Project on the Remote Server](#setting-up-the-project-on-the-remote-server)
    - [Configuring Gunicorn](#configuring-gunicorn)
    - [Configuring NGINX](#configuring-nginx)
    - [Setting up Continuous Deployment](#setting-up-continuous-deployment)
    - [Activate SSM Agent](#activate-ssm-agent)
    - [Create SSM Role](#create-ssm-role)
    - [Assign SSM Role to EC2 Instance](#assign-ssm-role-to-ec2-instance)
    - [Github Secrets](#github-secrets)
    - [Deployment Script](#deployment-script)
    - [yml File](#yml-file)
    - [Issues with Github Actions](#issues-with-github-actions)
    - [Activate SSM Agent](#activate-ssm-agent)
    - [Create SSM Role](#create-ssm-role)
    - [Assign SSM Role to EC2 Instance](#assign-ssm-role-to-ec2-instance)
    - [Github Secrets](#github-secrets)
    - [Deployment Script](#deployment-script)
    - [yml File](#yml-file)
    - [Issues with Github Actions](#issues-with-github-actions)
    - [Setting up your Domain](#setting-up-your-domain)
    - [Creating Domain records](#creating-domain-records)
    - [Configuring our Web Server](#configuring-our-web-server)
    - [Creating Domain records](#creating-domain-records)
    - [Configuring our Web Server](#configuring-our-web-server)
    - [HTTPS](#https)
    - [Installing Certbot](#installing-certbot)
    - [Installing Certbot](#installing-certbot)
    - [Closing Thoughts](#closing-thoughts)
    ---
    ## Project Layout
    Here is the project layout:
    ```
    django-app
    |___ backend/ (Django Backend settings)
    | |___ settings.py
    |___ static_files/
    |___ templates/ (Django Templates)
    | |___ index.html
    |___ manage.py
    |___ requirements.txt
    ```
    ---
    ### Previewing the Project
    Start by cloning the project with the command:
    ```
    $ git clone https://github.com/rmiyazaki6499/django-app.git
    ```

    ## Previewing the `django-app` project with Docker
    ### Install Docker

    To make this as easy as possible, we will be using *Docker Compose* to creat our container.


    ---

    ## Project Layout

    Here is the project layout:

    ```
    django-app
    |___ backend/ (Django Backend settings)
    | |___ settings.py
    |___ static_files/
    |___ templates/ (Django Templates)
    | |___ index.html
    |___ manage.py
    |___ requirements.txt
    ```

    ---

    ### Previewing the Project

    Start by cloning the project with the command:

    ```
    $ git clone https://github.com/rmiyazaki6499/django-app.git
    ```

    ## Previewing the `django-app` project with Docker

    ### Install Docker

    To make this as easy as possible, we will be using *Docker Compose* to creat our container.

    - If you do not have Docker yet, start by downloading it if you are on a Mac or Windows:
    https://www.docker.com/products/docker-desktop
    https://www.docker.com/products/docker-desktop

    - Or if you are on a Linux Distribution follow the directions here:
    https://docs.docker.com/compose/install/
    https://docs.docker.com/compose/install/

    - To confirm you have Docker Compose, open up your terminal and run the command below:

    ```
    $ docker-compose --version
    docker-compose version 1.26.2, build eefe0d31
    ```
    ```
    $ docker-compose --version
    docker-compose version 1.26.2, build eefe0d31
    ```

    - Go into the project directory to build and run the container with:

    ```
    $ cd mern-app/
    $ docker-compose up --build
    ```
    ```
    $ cd mern-app/
    $ docker-compose up --build
    ```

    - Navigate to http://localhost:8000, you should see something like this:

    ![django-default](https://user-images.githubusercontent.com/41876764/87993902-8d27df00-caa0-11ea-8f66-990932b37ca3.png)
    ### Cleaning up the Container and Image
    ![django-default](https://user-images.githubusercontent.com/41876764/87993902-8d27df00-caa0-11ea-8f66-990932b37ca3.png)

    ### Cleaning up the Container and Image

    - To stop the container from running, use `<Ctrl-C>` twice.
    - To close down the container use the command:

    ```
    $ docker-compose down
    ```
    ```
    $ docker-compose down
    ```

    - Then to clean up the container and image which we are no longer using use the command:

    ```
    $ docker system prune -fa
    ```
    ```
    $ docker system prune -fa
    ```

    - Confirm that the container and image is no longer there with:

    ```
    $ docker system df -v
    ```
    ```
    $ docker system df -v
    ```

    ## Previewing the `django-app` project locally
    ## Previewing the `django-app` project locally

    To preview the project on your local machine, follow the directions below.

    To preview the project on your local machine, follow the directions below.

    - Create a virtual environment:
    ```
    $ cd django-app/
    $ python3 -m venv env
    $ source env/bin/activate
    ```

    ```
    $ cd django-app/
    $ python3 -m venv env
    $ source env/bin/activate
    ```

    - Install dependencies for Django (make sure you have pip (https://pip.pypa.io/en/stable/installing/)):

    ```
    $ pip3 install -r requirements.txt
    ```
    ```
    $ pip3 install -r requirements.txt
    ```

    - Migrate the database and collect the static files:
    ```
    $ python3 manage.py makemigrations
    $ python3 manage.py migrate
    $ python3 manage.py collectstatic
    ```

    ```
    $ python3 manage.py makemigrations
    $ python3 manage.py migrate
    $ python3 manage.py collectstatic
    ```

    - Run the development server with the following command:

    ```
    $ python3 manage.py runserver
    ```
    ```
    $ python3 manage.py runserver
    ```

    - Run the production server with the following command:

    ```
    $ ENV_PATH=.env-prod python3 manage.py runserver
    ```
    ```
    $ ENV_PATH=.env-prod python3 manage.py runserver
    ```

    Navigate to http://localhost:8000 to view the site on the local server.
    It should look something like this:

    Navigate to http://localhost:8000 to view the site on the local server.
    It should look something like this:
    ![django-default](https://user-images.githubusercontent.com/41876764/87993902-8d27df00-caa0-11ea-8f66-990932b37ca3.png)

    ![django-default](https://user-images.githubusercontent.com/41876764/87993902-8d27df00-caa0-11ea-8f66-990932b37ca3.png)

    ___

    ## Creating an AWS Account

    Why choose AWS?
    ___

    ## Creating an AWS Account

    Why choose AWS?

    - It offers a lot of free services for new accounts.
    - Very popular among startups and even enterprises.
    - Customer service support, in our opinion, is a step above the competition.
    - If you do not have an account, check out this step by step guide by Amazon [here](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/).

    Before you provision a new server, it is best practice to make sure your account is as secure as possible by following the prompts on your Security Status checklist. This can be found under the IAM tab from your console's homepage.
    ![security_status](https://user-images.githubusercontent.com/41876764/86527279-47d5a180-be52-11ea-97e0-537b62a987b7.png)
    ---
    ## Creating an AWS EC2 Instance
    Amazon's EC2 or Elastic Compute Cloud is one of the core products/services AWS provides and is the main building block for many of AWS's other services. It allows users to essentially rent virtual computers on which to run their own applications. You can learn more about EC2 [here](https://en.wikipedia.org/wiki/Amazon_Elastic_Compute_Cloud).
    Before you provision a new server, it is best practice to make sure your account is as secure as possible by following the prompts on your Security Status checklist. This can be found under the IAM tab from your console's homepage.

    ![security_status](https://user-images.githubusercontent.com/41876764/86527279-47d5a180-be52-11ea-97e0-537b62a987b7.png)

    ---

    ## Creating an AWS EC2 Instance

    Amazon's EC2 or Elastic Compute Cloud is one of the core products/services AWS provides and is the main building block for many of AWS's other services. It allows users to essentially rent virtual computers on which to run their own applications. You can learn more about EC2 [here](https://en.wikipedia.org/wiki/Amazon_Elastic_Compute_Cloud).

    Start out by going into the AWS Console and going to the EC2 tab. An easy way to get there is through the Services link at the top and search for EC2 in the prompt.

    *We recommend setting your AWS Region to the one closest to you or your intended audience. However, please note that not all AWS Services will be available depending on the region. For our example, we will be working out of the us-east-1 as this region supports all AWS Services.*
    ---
    ### _EC2 Console_
    You should see this screen (as of July 2020):
    ![ec2_console](https://user-images.githubusercontent.com/41876764/86527285-5a4fdb00-be52-11ea-9de2-8ad9dabfd9f3.png)
    Go to the **Running Instances** link on the EC2 dashboard and click Launch Instance.
    ![ec2_running_instances](https://user-images.githubusercontent.com/41876764/86527322-c8949d80-be52-11ea-9bcb-83ab9a1c8ac6.png)
    ---
    ### _AMI_
    In this step, AWS will prompt you to choose an AMI. AMI's are templates to configure new instances. For this tutorial, we will be using Ubuntu 18.04 64-bit (free tier).
    ![ec2_choose_ami](https://user-images.githubusercontent.com/41876764/86527338-dba76d80-be52-11ea-834b-f0576918cc40.png)
    Next, select the **t2.micro** instance type.
    ![ec2_choose_instance_type](https://user-images.githubusercontent.com/41876764/86527344-eb26b680-be52-11ea-8636-3c49552b5872.png)
    On the next screen, keep clicking next until you see the option to **Configure Security Group**.
    ---
    ### _Security Groups_
    Security groups are virtual firewalls for your instances.

    **Important:** *by default, there is an implicit deny on all ports meaning if you do not add rules, all incoming/outgoing traffic is blocked. Security groups are also stateful, which means setting inbound rules for a certain port will also affect the outbound rules for that port.*

    Set your Security Group settings with the following:
    ![django_security_groups](https://user-images.githubusercontent.com/41876764/87994611-45a25280-caa2-11ea-8239-a1e409e4000a.png)
    *Setting Anywhere on Source for Custom TCP will display a warning flag, but you can ignore that for this tutorial. Ideally, you only want to set known IP addresses.*

    ---

    ### _EC2 Console_

    You should see this screen (as of July 2020):

    ![ec2_console](https://user-images.githubusercontent.com/41876764/86527285-5a4fdb00-be52-11ea-9de2-8ad9dabfd9f3.png)

    Go to the **Running Instances** link on the EC2 dashboard and click Launch Instance.

    ![ec2_running_instances](https://user-images.githubusercontent.com/41876764/86527322-c8949d80-be52-11ea-9bcb-83ab9a1c8ac6.png)

    ---

    ### _AMI_

    In this step, AWS will prompt you to choose an AMI. AMI's are templates to configure new instances. For this tutorial, we will be using Ubuntu 18.04 64-bit (free tier).

    ![ec2_choose_ami](https://user-images.githubusercontent.com/41876764/86527338-dba76d80-be52-11ea-834b-f0576918cc40.png)

    Next, select the **t2.micro** instance type.

    ![ec2_choose_instance_type](https://user-images.githubusercontent.com/41876764/86527344-eb26b680-be52-11ea-8636-3c49552b5872.png)

    On the next screen, keep clicking next until you see the option to **Configure Security Group**.

    ---

    ### _Security Groups_

    Security groups are virtual firewalls for your instances.

    **Important:** *by default, there is an implicit deny on all ports meaning if you do not add rules, all incoming/outgoing traffic is blocked. Security groups are also stateful, which means setting inbound rules for a certain port will also affect the outbound rules for that port.*

    Set your Security Group settings with the following:

    ![django_security_groups](https://user-images.githubusercontent.com/41876764/87994611-45a25280-caa2-11ea-8239-a1e409e4000a.png)

    *Setting Anywhere on Source for Custom TCP will display a warning flag, but you can ignore that for this tutorial. Ideally, you only want to set known IP addresses.*

    | Type | Port Range | Description. |
    | ------------ | :--------: | ------------------------------------------: |
    | SSH | 22 | Port for SSH'ing into your server |
    | HTTP | 80 | Port for HTTP requests to your web server |
    | HTTPS | 443 | Port for HTTPS requests to your web server |
    | Custom TCP | 8000 | Port which Django will run |
    | Custom TCP | 5432 | Port at which to connect to PostgreSQL |
    As you can see with the warning near the bottom of the screen, you do not want to set your *SSH Source IP* as anywhere. This will create a security vulnerability as anyone can try to attempt to log into your server.

    Therefore, be sure to set it to your own IP address and any other IP address which may need access to the instance.
    ---
    ### _Instance Details_
    Click forward to **Review and Launch** to view all configurations of your Instance/AMI.
    If the configurations look correct go ahead and hit **Launch**.
    ---
    ### _Key Pairs_
    Once you launch the instance, AWS will prompt you to create a key pair. A key pair consists of a public key that AWS stores and a private key file that you store. Together they allow you to connect to your instance securely through asymmetrical encryption.

    If this is the first time you are creating a key pair for your project, select **Create a new key pair** from the drop-down and add a name for the key pair.

    *Be sure to store the key pair in a secure location. It is generated only once and AWS will not have access to it if you lose it. This is your only means to log into the EC2 instance via SSH.*
    ![key_pair](https://user-images.githubusercontent.com/41876764/86527366-0bef0c00-be53-11ea-9f2c-570e3daa3105.png)
    Once you have downloaded the **key pair** make sure to move the **.pem** file to the root directory of your project on your local computer.
    ![mern-app_root_w_pem](https://user-images.githubusercontent.com/41876764/86527373-17423780-be53-11ea-91fe-fa2a108cc937.png)
    Next, check the checkbox acknowledging that you have access to the private key pair and click Launch Instances. This should take you to the Launch Status page.
    ---
    ## Accessing your EC2 Instance
    Click on the Instances tab on your EC2 console.
    ![ec2_instance_first_initializing](https://user-images.githubusercontent.com/41876764/86527378-21643600-be53-11ea-8323-f92d50ed00a8.png)
    The instance may take a couple of minutes to launch. Once it passes its' status checks, the instance state should show a green circle and a "running" state.
    ---
    ### _Elastic IP_
    Before you can log into your EC2 instance, it is important to first generate an Elastic IP and associate it to your EC2 instance.

    An Elastic IP is a dedicated IP address for your EC2 instance. Although the instance has a public IP address assigned upon creation, that IP address is dynamic and does not persist if you stop and start the instance. With an Elastic IP address, you can mask the failure of an instance by remapping the address to another instance in your account.

    Therefore, by using an Elastic IP, you can have a dedicated IP to which users from the internet can access your instance. This will come in handy later when you assign a custom domain name and add SSL certification to the server.

    *Note: If you are using the free tier, AWS will charge you if your Elastic IP is NOT associated with an AWS identity.*

    On the EC2 dashboard, look under the **Network & Security** tab and go to **Elastic IPs**:
    ![elastic_ips_link](https://user-images.githubusercontent.com/41876764/86527387-2e812500-be53-11ea-92c6-806ecc97ae2c.png)
    It should take you here:
    ![elastic_ip_addresses](https://user-images.githubusercontent.com/41876764/86527390-393bba00-be53-11ea-8c83-4496e091e78c.png)

    Click on **Allocate Elastic IP address**.

    It should take you here:
    ![allocate_ip_address](https://user-images.githubusercontent.com/41876764/86527396-422c8b80-be53-11ea-83a2-8c8b49963bbf.png)
    Select **Allocate**.
    ![elastic_ip_created](https://user-images.githubusercontent.com/41876764/86527403-4f497a80-be53-11ea-8649-d00eded3a2dc.png)
    This should create an Elastic IP. The next step is to associate that Elastic IP to the instance.

    With the Elastic IP checked on the left side:

    As you can see with the warning near the bottom of the screen, you do not want to set your *SSH Source IP* as anywhere. This will create a security vulnerability as anyone can try to attempt to log into your server.

    Therefore, be sure to set it to your own IP address and any other IP address which may need access to the instance.

    ---

    ### _Instance Details_

    Click forward to **Review and Launch** to view all configurations of your Instance/AMI.
    If the configurations look correct go ahead and hit **Launch**.

    ---

    ### _Key Pairs_

    Once you launch the instance, AWS will prompt you to create a key pair. A key pair consists of a public key that AWS stores and a private key file that you store. Together they allow you to connect to your instance securely through asymmetrical encryption.

    If this is the first time you are creating a key pair for your project, select **Create a new key pair** from the drop-down and add a name for the key pair.

    *Be sure to store the key pair in a secure location. It is generated only once and AWS will not have access to it if you lose it. This is your only means to log into the EC2 instance via SSH.*

    ![key_pair](https://user-images.githubusercontent.com/41876764/86527366-0bef0c00-be53-11ea-9f2c-570e3daa3105.png)

    Once you have downloaded the **key pair** make sure to move the **.pem** file to the root directory of your project on your local computer.

    ![mern-app_root_w_pem](https://user-images.githubusercontent.com/41876764/86527373-17423780-be53-11ea-91fe-fa2a108cc937.png)

    Next, check the checkbox acknowledging that you have access to the private key pair and click Launch Instances. This should take you to the Launch Status page.

    ---

    ## Accessing your EC2 Instance

    Click on the Instances tab on your EC2 console.

    ![ec2_instance_first_initializing](https://user-images.githubusercontent.com/41876764/86527378-21643600-be53-11ea-8323-f92d50ed00a8.png)

    The instance may take a couple of minutes to launch. Once it passes its' status checks, the instance state should show a green circle and a "running" state.

    ---

    ### _Elastic IP_

    Before you can log into your EC2 instance, it is important to first generate an Elastic IP and associate it to your EC2 instance.

    An Elastic IP is a dedicated IP address for your EC2 instance. Although the instance has a public IP address assigned upon creation, that IP address is dynamic and does not persist if you stop and start the instance. With an Elastic IP address, you can mask the failure of an instance by remapping the address to another instance in your account.

    Therefore, by using an Elastic IP, you can have a dedicated IP to which users from the internet can access your instance. This will come in handy later when you assign a custom domain name and add SSL certification to the server.

    *Note: If you are using the free tier, AWS will charge you if your Elastic IP is NOT associated with an AWS identity.*

    On the EC2 dashboard, look under the **Network & Security** tab and go to **Elastic IPs**:

    ![elastic_ips_link](https://user-images.githubusercontent.com/41876764/86527387-2e812500-be53-11ea-92c6-806ecc97ae2c.png)

    It should take you here:

    ![elastic_ip_addresses](https://user-images.githubusercontent.com/41876764/86527390-393bba00-be53-11ea-8c83-4496e091e78c.png)

    Click on **Allocate Elastic IP address**.

    It should take you here:

    ![allocate_ip_address](https://user-images.githubusercontent.com/41876764/86527396-422c8b80-be53-11ea-83a2-8c8b49963bbf.png)

    Select **Allocate**.

    ![elastic_ip_created](https://user-images.githubusercontent.com/41876764/86527403-4f497a80-be53-11ea-8649-d00eded3a2dc.png)

    This should create an Elastic IP. The next step is to associate that Elastic IP to the instance.

    With the Elastic IP checked on the left side:

    - Go to Actions
    - Click on **Associate Elastic IP address**
    - Make sure your Resource type is Instance
    - Search for your instance (if this is your first time, it should be the only one)
    - Click **Associate**

    To check if everything is done correctly, go to the Instances tab and in the instance details, you should see the Elastic IP.
    ---
    ### _Connecting to your EC2 Instance_
    With the instance selected in the EC2 console, click Connect near the top. You will be prompted with directions on how to connect to your EC2 instance:
    ![connect_to_your_instance](https://user-images.githubusercontent.com/41876764/86527414-5b353c80-be53-11ea-975f-c2f53c3e7de8.png)
    To check if everything is done correctly, go to the Instances tab and in the instance details, you should see the Elastic IP.

    ---

    ### _Connecting to your EC2 Instance_

    With the instance selected in the EC2 console, click Connect near the top. You will be prompted with directions on how to connect to your EC2 instance:

    ![connect_to_your_instance](https://user-images.githubusercontent.com/41876764/86527414-5b353c80-be53-11ea-975f-c2f53c3e7de8.png)

    - Changing the .pem file's permission to read-only ensures nobody can modify your private key.
    ---
    ### _Connecting to your EC2 Instance_
    Now that we have our instance and have allocated an Elastic IP to it.
    It is time to connect to our server!
    If you have not already, go to the **Instances** link in the EC2 Dashboard.
    With the instance highlighted, click on **Connect** on the top banner of the Instaces Dashboard.
    It should give you a pop up with directions on how to connect to your EC2 instance.
    ![connect_to_your_instance](https://user-images.githubusercontent.com/41876764/86527414-5b353c80-be53-11ea-975f-c2f53c3e7de8.png)
    Go back to your project root directory and make sure that your **.pem** file has the correct permissions.
    Run the command:
    ```
    $ chmod 400 *.pem
    ```
    Next run the command given to you in the example:
    ```
    $ ssh -i "<KEY-PAIR>.pem" ubuntu@<YOUR-IP-ADDRESS>.compute-1.amazonaws.com
    ```
    The ssh should prompt you that the authenticity of host *instance* can't be established and will show an ECDSA key fingerprint.
    It will also ask you `Are you sure you want to continue connecting (yes/no)?`
    Type `yes` and *Enter*.
    This should take you into the EC2 Instance.
    If not, try the `ssh` command again.
    Congratulations you are inside your EC2 Instance!
    ---
    ## EC2 Environment Setup
    Once you are logged into your server, we will install all of the project dependencies:
    We will install the following:

    ---

    ### _Connecting to your EC2 Instance_

    Now that we have our instance and have allocated an Elastic IP to it.
    It is time to connect to our server!

    If you have not already, go to the **Instances** link in the EC2 Dashboard.

    With the instance highlighted, click on **Connect** on the top banner of the Instaces Dashboard.

    It should give you a pop up with directions on how to connect to your EC2 instance.

    ![connect_to_your_instance](https://user-images.githubusercontent.com/41876764/86527414-5b353c80-be53-11ea-975f-c2f53c3e7de8.png)

    Go back to your project root directory and make sure that your **.pem** file has the correct permissions.

    Run the command:
    ```
    $ chmod 400 *.pem
    ```

    Next run the command given to you in the example:
    ```
    $ ssh -i "<KEY-PAIR>.pem" ubuntu@<YOUR-IP-ADDRESS>.compute-1.amazonaws.com
    ```

    The ssh should prompt you that the authenticity of host *instance* can't be established and will show an ECDSA key fingerprint.
    It will also ask you `Are you sure you want to continue connecting (yes/no)?`

    Type `yes` and *Enter*.

    This should take you into the EC2 Instance.
    If not, try the `ssh` command again.

    Congratulations you are inside your EC2 Instance!

    ---

    ## EC2 Environment Setup

    Once you are logged into your server, we will install all of the project dependencies:
    We will install the following:
    - Django and Python3
    - pip
    - gunicorn
    - NGINX
    - UFW (Firewall)
    ---
    ### _Firewall Setup_
    **Enable Firewall and allow OpenSSH**
    ```
    % sudo ufw enable
    % sudo ufw allow OpenSSH
    % sudo ufw allow 'Nginx Full'
    ```
    Check to make sure we are allowing OpenSSH
    ```
    % sudo ufw status
    ```
    Expected output:
    ```
    To Action From
    -- ------ ----
    Nginx Full ALLOW Anywhere
    OpenSSH ALLOW Anywhere
    Nginx Full (v6) ALLOW Anywhere (v6)
    OpenSSH (v6) ALLOW Anywhere (v6)
    ```
    ---
    ## Installing Dependencies
    Updating packages:
    ```
    % sudo apt update
    % sudo apt upgrade
    ```
    Installing Python 3, PostgreSQL, NGINX and Gunicorn:
    ```
    % sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx gunicorn curl
    ```
    ---
    ### _Setting up the Project on the Remote Server_
    Recall the steps earlier we did with the dummy project on our local machine. We will repeat that on the EC2 instance.

    ```
    % git clone https://github.com/rmiyazaki6499/django-app.git
    % python3 -m venv env
    % source env/bin/activate
    % pip3 install -r requirements.txt
    % python3 manage.py makemigrations
    % python3 manage.py migrate
    % python3 manage.py collectstatic
    ```

    **Note: Make sure to update your .env file so that your project has the correct Environment Varialbes necessary to run.**

    ---
    ### _Configuring Gunicorn_
    Create a gunicorn.socket file:
    ```
    % sudo vim /etc/systemd/system/gunicorn.socket
    ```
    Configure the gunicorn.socket file with:
    ```
    [Unit]
    Description=gunicorn socket
    [Socket]
    ListenStream=/run/gunicorn.sock
    [Install]
    WantedBy=sockets.target
    ```
    Next configure the gunicorn.service file with:
    ```
    % sudo nano /etc/systemd/system/gunicorn.service
    ```
    Use the configurations below:
    ```
    [Unit]
    Description=gunicorn daemon
    Requires=gunicorn.socket
    After=network.target
    [Service]
    User=djangoadmin
    Group=www-data
    WorkingDirectory=/home/ubuntu/<YOUR-PROJECT>
    ExecStart=/home/djangoadmin/pyapps/venv/bin/gunicorn \
    --access-logfile - \
    --workers 3 \
    --bind unix:/run/gunicorn.sock \
    <YOUR-PROJECT>.wsgi:application
    [Install]
    WantedBy=multi-user.target
    ```
    Start and enable Gunicorn:
    ```
    % sudo systemctl start gunicorn.socket
    % sudo systemctl enable gunicorn.socket
    ```
    Check the status of gunicorn with:
    ```
    % sudo systemctl status gunicorn.socket
    ```
    ---
    ### _Configuring NGINX_
    Next, we need to configure NGINX to redirect web traffic.

    Create a new NGINX config file with the following command:
    ```
    % sudo vim /etc/nginx/sites-available/<YOUR-PROJECT-NAME>
    ```
    Paste in the following configurations and replace any of the ALL CAPS sections with your own project details:
    ```
    server {
    listen 80;
    server_name YOUR_INSTANCE_IP_ADDRESS;
    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
    root /home/ubuntu/<YOUR-PROJECT>;
    }
    location /media/ {
    root /home/ubuntu/<YOUR-PROJECT>;
    }
    location / {
    include proxy_params;
    proxy_pass http://unix:/run/gunicorn.sock;
    }
    }
    ```
    Once your NGINX config is set up, make sure there are no syntax errors with:

    ```
    % sudo nginx -t
    ```
    Next, create a soft link of your config file from sites-available to the sites-enabled directory. This step is important because NGINX will use the configuration settings located at /etc/nginx/sites-available/default by default if there is nothing in sites-enabled.
    ```
    % sudo ln -s /etc/nginx/sites-available/<YOUR-PROJECT-NAME> /etc/nginx/sites-enabled
    ```
    Restart the NGINX Web Server with:
    ```
    % sudo systemctl restart nginx
    ```
    Now if you go to your Elastic IP on your browser it should show the app!
    [Back to Table of Contents](#table-of-contents)
    ---
    ## Setting up Continuous Deployment
    Continuous Deployment is helpful because it saves you the time of having to ssh into your EC2 instance each time you make an update on your codebase.
    In this project, we will be using a Github Action called [AWS SSM Send-Command](https://github.com/marketplace/actions/aws-ssm-send-command) created by **peterkimzz** to implement auto-deployment.
    ### _Github Actions_
    Github Actions is a service by Github that allows you to perform actions such as run scripts every time something happens to a repository. In our case, we will run a script to install the latest dependencies and restart our server every time a push is made to master.

    For Github Actions to work, it needs a way to communicate with the EC2 Instance and vice-versa. In order to do that, we need to assign permissions via IAM roles.
    ---
    ### _Create SSM Role_
    To create an **IAM Role** with `AmazonSSMFullAccess` permissions:

    ---

    ### _Firewall Setup_

    **Enable Firewall and allow OpenSSH**

    ```
    % sudo ufw enable
    % sudo ufw allow OpenSSH
    % sudo ufw allow 'Nginx Full'
    ```

    Check to make sure we are allowing OpenSSH

    ```
    % sudo ufw status
    ```

    Expected output:

    ```
    To Action From
    -- ------ ----
    Nginx Full ALLOW Anywhere
    OpenSSH ALLOW Anywhere
    Nginx Full (v6) ALLOW Anywhere (v6)
    OpenSSH (v6) ALLOW Anywhere (v6)
    ```

    ---

    ## Installing Dependencies

    Updating packages:

    ```
    % sudo apt update
    % sudo apt upgrade
    ```

    Installing Python 3, PostgreSQL, NGINX and Gunicorn:

    ```
    % sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx gunicorn curl
    ```

    ---

    ### _Setting up the Project on the Remote Server_

    Recall the steps earlier we did with the dummy project on our local machine. We will repeat that on the EC2 instance.

    ```
    % git clone https://github.com/rmiyazaki6499/django-app.git
    % python3 -m venv env
    % source env/bin/activate
    % pip3 install -r requirements.txt
    % python3 manage.py makemigrations
    % python3 manage.py migrate
    % python3 manage.py collectstatic
    ```

    **Note: Make sure to update your .env file so that your project has the correct Environment Varialbes necessary to run.**

    ---

    ### _Configuring Gunicorn_

    Create a gunicorn.socket file:

    ```
    % sudo vim /etc/systemd/system/gunicorn.socket
    ```

    Configure the gunicorn.socket file with:

    ```
    [Unit]
    Description=gunicorn socket
    [Socket]
    ListenStream=/run/gunicorn.sock
    [Install]
    WantedBy=sockets.target
    ```

    Next configure the gunicorn.service file with:

    ```
    % sudo nano /etc/systemd/system/gunicorn.service
    ```

    Use the configurations below:

    ```
    [Unit]
    Description=gunicorn daemon
    Requires=gunicorn.socket
    After=network.target
    [Service]
    User=djangoadmin
    Group=www-data
    WorkingDirectory=/home/ubuntu/<YOUR-PROJECT>
    ExecStart=/home/djangoadmin/pyapps/venv/bin/gunicorn \
    --access-logfile - \
    --workers 3 \
    --bind unix:/run/gunicorn.sock \
    <YOUR-PROJECT>.wsgi:application
    [Install]
    WantedBy=multi-user.target
    ```

    Start and enable Gunicorn:

    ```
    % sudo systemctl start gunicorn.socket
    % sudo systemctl enable gunicorn.socket
    ```

    Check the status of gunicorn with:

    ```
    % sudo systemctl status gunicorn.socket
    ```

    ---

    ### _Configuring NGINX_

    Next, we need to configure NGINX to redirect web traffic.

    Create a new NGINX config file with the following command:

    ```
    % sudo vim /etc/nginx/sites-available/<YOUR-PROJECT-NAME>
    ```

    Paste in the following configurations and replace any of the ALL CAPS sections with your own project details:

    ```
    server {
    listen 80;
    server_name YOUR_INSTANCE_IP_ADDRESS;
    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
    root /home/ubuntu/<YOUR-PROJECT>;
    }
    location /media/ {
    root /home/ubuntu/<YOUR-PROJECT>;
    }
    location / {
    include proxy_params;
    proxy_pass http://unix:/run/gunicorn.sock;
    }
    }
    ```

    Once your NGINX config is set up, make sure there are no syntax errors with:

    ```
    % sudo nginx -t
    ```

    Next, create a soft link of your config file from sites-available to the sites-enabled directory. This step is important because NGINX will use the configuration settings located at /etc/nginx/sites-available/default by default if there is nothing in sites-enabled.

    ```
    % sudo ln -s /etc/nginx/sites-available/<YOUR-PROJECT-NAME> /etc/nginx/sites-enabled
    ```

    Restart the NGINX Web Server with:

    ```
    % sudo systemctl restart nginx
    ```

    Now if you go to your Elastic IP on your browser it should show the app!

    [Back to Table of Contents](#table-of-contents)

    ---

    ## Setting up Continuous Deployment

    Continuous Deployment is helpful because it saves you the time of having to ssh into your EC2 instance each time you make an update on your codebase.

    In this project, we will be using a Github Action called [AWS SSM Send-Command](https://github.com/marketplace/actions/aws-ssm-send-command) created by **peterkimzz** to implement auto-deployment.

    ### _Github Actions_

    Github Actions is a service by Github that allows you to perform actions such as run scripts every time something happens to a repository. In our case, we will run a script to install the latest dependencies and restart our server every time a push is made to master.

    For Github Actions to work, it needs a way to communicate with the EC2 Instance and vice-versa. In order to do that, we need to assign permissions via IAM roles.

    ---

    ### _Create SSM Role_

    To create an **IAM Role** with `AmazonSSMFullAccess` permissions:
    - Open the IAM console at https://console.aws.amazon.com/iam/.
    - In the navigation panel, select **Roles**, and then click **Create role**.
    - Under *Select type of trusted entity*, choose **AWS service**.
    - In the *Choose a use case* section, choose **EC2**, and then choose **Next: Permissions**.
    - On the Attached permissions policy page, search for the `AmazonSSMFullAccess` policy, choose it, and then choose **Next: Review**.
    - On the **Review** page, type a name in the Role name box, and then type a description.
    - Choose **Create role**. The system returns you to the Roles page.
    ---
    ### _Assigning an SSM Role to EC2 Instance_
    Once you have the **Role** created:

    ---

    ### _Assigning an SSM Role to EC2 Instance_

    Once you have the **Role** created:
    - Go to the **EC2 Instance Dashboard**
    - Go to the **Instances** link
    - Highlight the Instance
    @@ -611,211 +611,211 @@ Start out by going into the AWS Console and going to the EC2 tab. An easy way to
    - **Attach/Replace IAM Role**
    - Select the SSM Role you had created earlier
    - Hit **Apply** to save changes
    ---
    ### _Github Secrets_
    With our instance being able to use the SSM Agent, we will need to provide it some details so that it can access our EC2 instance.
    Now that the instance is able to communicate to Github via SSM Agent, you will need to provide the repo with credentials. Github Secrets act like environment variables for repositories and store sensitive data such as AWS login information. In order for the Github Actions script to work, it needs these three secrets: AWS_ACCESS_KEY, AWS_SECRET_ACCESS_KEY, and INSTANCE_ID.

    There is an article by AWS on how to find your AWS Access Key and Secret Access Key [here](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys). Your instance ID is shown on your instances tab under EC2.
    Start by going to your Github project repo:

    ---

    ### _Github Secrets_

    With our instance being able to use the SSM Agent, we will need to provide it some details so that it can access our EC2 instance.

    Now that the instance is able to communicate to Github via SSM Agent, you will need to provide the repo with credentials. Github Secrets act like environment variables for repositories and store sensitive data such as AWS login information. In order for the Github Actions script to work, it needs these three secrets: AWS_ACCESS_KEY, AWS_SECRET_ACCESS_KEY, and INSTANCE_ID.

    There is an article by AWS on how to find your AWS Access Key and Secret Access Key [here](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys). Your instance ID is shown on your instances tab under EC2.

    Start by going to your Github project repo:
    - Then go to your **Settings**
    - On the menu on the left, look for the link for **Secrets**
    - There, add the three *Secrets* with these keys:
    - `AWS_ACCESS_KEY_ID`
    - `AWS_SECRET_ACCESS_KEY`
    - `INSTANCE_ID`
    Once these **Secrets** are set, we are ready to move on!
    ---
    ### _Deployment script_
    Next, let's create a bash script to download dependencies and restart NGINX and PM2. Inside the EC2 instance, create a deploy.sh script in the root of the directory:
    ```
    % vim deploy.sh
    ```
    Fill the contents with the bash commands we ran to build the project earlier:
    ```
    #!/bin/sh
    sudo git pull origin master
    sudo pip3 install -r requirements.txt
    python3 manage.py makemigrations
    python3 manage.py migrate
    python3 manage.py collectstatic
    sudo systemctl restart nginx
    sudo systemctl restart gunicorn
    ```
    ---
    ### _YAML File_
    *AWS SSM Send-Command* requires a .yml file to execute. At the root of the project, create these two directories:
    ```
    % mkdir -p .github/workflows/
    ```
    Create a new YAML file with:
    ```
    % sudo vim .github/workflows/deploy.yml
    ```
    Paste in the following:
    ```
    name: Deploy using AWS SSM Send-Command
    on:
    push:
    branches: [master]
    jobs:
    start:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: AWS SSM Send Command
    uses: peterkimzz/[email protected]
    with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: us-east-1
    instance-ids: ${{ secrets.INSTANCE_ID }}
    comment: Deploy the master branch
    working-directory: /home/ubuntu/<YOUR PROJECT DIRECTORY>
    command: /bin/sh ./deploy.sh
    ```
    The Secrets we provided to the repo earlier comes into use in this script.

    There are 3 parts of the .yml file to configure:

    1. The aws-region should be the same region as where you have created your EC2 instance. (If you do not know, check the top left of your EC2 console to confirm the region you are in).
    2. working-directory should be the directory where you created the deploy.sh script.
    3. command should be the command you would like the SSM agent to run.

    Once this is complete, commit and push the workflow to your repo.
    ---
    ## Setting up your Domain
    So far, users can access the site using the Elastic IP. However, it can be difficult to remember and share so we will configure a custom domain name.

    To get started, you need to first purchase a domain. This can range from $10 to $1,000+s. Amazon has a service called Route53 you can use or you can choose other providers such as [Google Domains](https://domains.google/), [GoDaddy](https://www.godaddy.com/), etc. (we used Google for AlgoAcademy which was $10/year).

    There are two steps you would need to configure to connect the project with a custom domain:
    - `AWS_ACCESS_KEY_ID`
    - `AWS_SECRET_ACCESS_KEY`
    - `INSTANCE_ID`

    Once these **Secrets** are set, we are ready to move on!

    ---

    ### _Deployment script_

    Next, let's create a bash script to download dependencies and restart NGINX and PM2. Inside the EC2 instance, create a deploy.sh script in the root of the directory:

    ```
    % vim deploy.sh
    ```
    Fill the contents with the bash commands we ran to build the project earlier:
    ```
    #!/bin/sh
    sudo git pull origin master
    sudo pip3 install -r requirements.txt
    python3 manage.py makemigrations
    python3 manage.py migrate
    python3 manage.py collectstatic
    sudo systemctl restart nginx
    sudo systemctl restart gunicorn
    ```

    ---

    ### _YAML File_

    *AWS SSM Send-Command* requires a .yml file to execute. At the root of the project, create these two directories:

    ```
    % mkdir -p .github/workflows/
    ```

    Create a new YAML file with:

    ```
    % sudo vim .github/workflows/deploy.yml
    ```

    Paste in the following:

    ```
    name: Deploy using AWS SSM Send-Command
    on:
    push:
    branches: [master]
    jobs:
    start:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: AWS SSM Send Command
    uses: peterkimzz/[email protected]
    with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: us-east-1
    instance-ids: ${{ secrets.INSTANCE_ID }}
    comment: Deploy the master branch
    working-directory: /home/ubuntu/<YOUR PROJECT DIRECTORY>
    command: /bin/sh ./deploy.sh
    ```
    The Secrets we provided to the repo earlier comes into use in this script.
    There are 3 parts of the .yml file to configure:
    1. The aws-region should be the same region as where you have created your EC2 instance. (If you do not know, check the top left of your EC2 console to confirm the region you are in).
    2. working-directory should be the directory where you created the deploy.sh script.
    3. command should be the command you would like the SSM agent to run.
    Once this is complete, commit and push the workflow to your repo.
    ---
    ## Setting up your Domain
    So far, users can access the site using the Elastic IP. However, it can be difficult to remember and share so we will configure a custom domain name.
    To get started, you need to first purchase a domain. This can range from $10 to $1,000+s. Amazon has a service called Route53 you can use or you can choose other providers such as [Google Domains](https://domains.google/), [GoDaddy](https://www.godaddy.com/), etc. (we used Google for AlgoAcademy which was $10/year).
    There are two steps you would need to configure to connect the project with a custom domain:
    - Create domain records with DNS registrar
    - Configure NGINX on the EC2 instance to recognize the domain
    ---
    ### _Creating Domain records_
    Let's start with configuring our DNS with records:
    ---
    ### _Creating Domain records_
    Let's start with configuring our DNS with records:
    - Go to the **DNS** portion of your registrar.
    - Find where you can create custom resource records.
    Set the records like so:
    | Name | Type | TTL | Data |
    | ---- | :---: | :-: | ----------------------: |
    | @ | A | 1h | YOUR-ELASTIC-IP-ADDRESS |
    | www | CNAME | 1h | your-awesome-site.com |
    ### _Configuring our Web Server_
    Edit the NGINX config file inside your EC2 instance:

    ```
    % sudo vim /etc/nginx/sites-available/default
    ```
    Update the `server:server_name` section of the config file:
    ```
    server {
    server_name <YOUR-ELASTIC-IP> your-awesome-site.com www.your-awesome-site.com;
    ...
    ```
    Save and restart NGINX:
    ```
    sudo sudo systemctl restart nginx
    ```
    *DNS changes can take up to 48 hours to update so your results may vary. Once it is complete, going to your custom domain should redirect you to your app.*
    ---
    ## HTTPS
    Secure Sockets Layer (SSL) is a standard security technology for establishing an encrypted link between a server and a client. So far, we have been serving web content over HTTP, which can be dangerous as data sent between the server and client is not encrypted. If you are handling user sign-in and need to protect data such as passwords or credit card information, it is always best practice to have SSL certification on your applications.

    In this tutorial, we will be using Certbot by letsencrypt.org, a non-profit organization that provides free SSL Certificates.
    ---
    ### _Installing Certbot_
    On your browser go to https://certbot.eff.org/instructions.
    Select the Software and Operating System (OS) you are using. In this case, we are using NGINX and Ubuntu 18.04 LTS (bionic).

    Inside your EC2 Instance, follow the command-line instructions until you see these instructions:

    ```
    % sudo certbot --nginx
    ```
    After running this command, Certbot will present to you the following prompt: Which names would you like to activate HTTPS for?

    If NGINX is configured correctly, it should show both your root domain as well as with the www subdomain:
    ```
    1: your-awesome-site.com
    2: www.your-awesome-site.com
    ```
    Select enter to activate both HTTP and HTTPs. The next prompt will be:
    ```
    Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    1: No redirect - Make no further changes to the web server configuration.
    2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
    new sites, or if you're confident your site works on HTTPS. You can undo this
    change by editing your web server's configuration.
    ```
    Select option 2 as this will redirect all traffic through HTTPS and is the most secure option. Afterward, Certbot will make changes to the NGINX configuration file.

    *Note: Once your site is using HTTPS, double-check your API calls and make sure that they are going to the https:// endpoint rather than http://. This may be an unnecessary precaution, but it is an easy bug to miss.*

    Next, go to your custom domain. Check to see if there is a lock icon next to your URL.
    ![secure_site](https://user-images.githubusercontent.com/41876764/86527267-2674b580-be52-11ea-9405-874f4f4ba7f0.png)
    Congratulations! You have successfully deployed a web app with HTTPS!
    ---
    ## Closing Thoughts
    I hope this provided some help for those getting started with web development and AWS. If you run into any problems, please feel free to reach out to either me or [Tu](https://github.com/tuvo1106/tuvo1106) and we can try our best to help. Thank you for reading!
    [Back to Table of Contents](#table-of-contents)
    Set the records like so:
    | Name | Type | TTL | Data |
    | ---- | :---: | :-: | ----------------------: |
    | @ | A | 1h | YOUR-ELASTIC-IP-ADDRESS |
    | www | CNAME | 1h | your-awesome-site.com |
    ### _Configuring our Web Server_
    Edit the NGINX config file inside your EC2 instance:
    ```
    % sudo vim /etc/nginx/sites-available/default
    ```
    Update the `server:server_name` section of the config file:
    ```
    server {
    server_name <YOUR-ELASTIC-IP> your-awesome-site.com www.your-awesome-site.com;
    ...
    ```
    Save and restart NGINX:
    ```
    sudo sudo systemctl restart nginx
    ```
    *DNS changes can take up to 48 hours to update so your results may vary. Once it is complete, going to your custom domain should redirect you to your app.*
    ---
    ## HTTPS
    Secure Sockets Layer (SSL) is a standard security technology for establishing an encrypted link between a server and a client. So far, we have been serving web content over HTTP, which can be dangerous as data sent between the server and client is not encrypted. If you are handling user sign-in and need to protect data such as passwords or credit card information, it is always best practice to have SSL certification on your applications.
    In this tutorial, we will be using Certbot by letsencrypt.org, a non-profit organization that provides free SSL Certificates.
    ---
    ### _Installing Certbot_
    On your browser go to https://certbot.eff.org/instructions.
    Select the Software and Operating System (OS) you are using. In this case, we are using NGINX and Ubuntu 18.04 LTS (bionic).
    Inside your EC2 Instance, follow the command-line instructions until you see these instructions:
    ```
    % sudo certbot --nginx
    ```
    After running this command, Certbot will present to you the following prompt: Which names would you like to activate HTTPS for?
    If NGINX is configured correctly, it should show both your root domain as well as with the www subdomain:
    ```
    1: your-awesome-site.com
    2: www.your-awesome-site.com
    ```
    Select enter to activate both HTTP and HTTPs. The next prompt will be:
    ```
    Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    1: No redirect - Make no further changes to the web server configuration.
    2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
    new sites, or if you're confident your site works on HTTPS. You can undo this
    change by editing your web server's configuration.
    ```
    Select option 2 as this will redirect all traffic through HTTPS and is the most secure option. Afterward, Certbot will make changes to the NGINX configuration file.
    *Note: Once your site is using HTTPS, double-check your API calls and make sure that they are going to the https:// endpoint rather than http://. This may be an unnecessary precaution, but it is an easy bug to miss.*
    Next, go to your custom domain. Check to see if there is a lock icon next to your URL.
    ![secure_site](https://user-images.githubusercontent.com/41876764/86527267-2674b580-be52-11ea-9405-874f4f4ba7f0.png)
    Congratulations! You have successfully deployed a web app with HTTPS!
    ---
    ## Closing Thoughts
    I hope this provided some help for those getting started with web development and AWS. If you run into any problems, please feel free to reach out to either me or [Tu](https://github.com/tuvo1106/tuvo1106) and we can try our best to help. Thank you for reading!
    [Back to Table of Contents](#table-of-contents)
  2. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 26, 2020. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -47,6 +47,7 @@ We have provided a dummy repo to follow along with [here](https://github.com/rmi
    - [Configuring our Web Server](#configuring-our-web-server)
    - [HTTPS](#https)
    - [Installing Certbot](#installing-certbot)
    - [Closing Thoughts](#closing-thoughts)


    ---
  3. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 26, 2020. 1 changed file with 5 additions and 2 deletions.
    7 changes: 5 additions & 2 deletions deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -476,8 +476,11 @@ Start out by going into the AWS Console and going to the EC2 tab. An easy way to
    ```
    % sudo nano /etc/systemd/system/gunicorn.service
    ```
    ```

    Use the configurations below:

    ```
    [Unit]
    Description=gunicorn daemon
    Requires=gunicorn.socket
    @@ -565,7 +568,7 @@ Start out by going into the AWS Console and going to the EC2 tab. An easy way to

    Now if you go to your Elastic IP on your browser it should show the app!

    [Back to Table of Contents](#table-of-contents)
    [Back to Table of Contents](#table-of-contents)

    ---

  4. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 26, 2020. 1 changed file with 5 additions and 6 deletions.
    11 changes: 5 additions & 6 deletions deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -29,10 +29,9 @@ We have provided a dummy repo to follow along with [here](https://github.com/rmi
    - [Elastic IP](#elastic-ip)
    - [Connecting to your EC2 Instance](#connecting-to-your-ec2-instance)
    - [EC2 Environment Setup](#ec2-environment-setup)
    - [Setup Web Server](#setup-web-server)
    - [Firewall Setup](#firewall-setup)
    - [Installing Software](#installing-software)
    - [Setting up our project](#setting-up-our-project)
    - [Installing Dependencies](#installing-dependencies)
    - [Setting up the Project on the Remote Server](#setting-up-the-project-on-the-remote-server)
    - [Configuring Gunicorn](#configuring-gunicorn)
    - [Configuring NGINX](#configuring-nginx)
    - [Setting up Continuous Deployment](#setting-up-continuous-deployment)
    @@ -526,7 +525,7 @@ Start out by going into the AWS Console and going to the EC2 tab. An easy way to
    Paste in the following configurations and replace any of the ALL CAPS sections with your own project details:

    ```
    server {
    server {
    listen 80;
    server_name YOUR_INSTANCE_IP_ADDRESS;
    @@ -543,10 +542,10 @@ server {
    include proxy_params;
    proxy_pass http://unix:/run/gunicorn.sock;
    }
    }
    }
    ```

    Once your NGINX config is set up, make sure there are no syntax errors with:
    Once your NGINX config is set up, make sure there are no syntax errors with:

    ```
    % sudo nginx -t
  5. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 26, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -189,7 +189,7 @@ We have provided a dummy repo to follow along with [here](https://github.com/rmi

    ---

    ## Create an AWS EC2 Instance
    ## Creating an AWS EC2 Instance

    Amazon's EC2 or Elastic Compute Cloud is one of the core products/services AWS provides and is the main building block for many of AWS's other services. It allows users to essentially rent virtual computers on which to run their own applications. You can learn more about EC2 [here](https://en.wikipedia.org/wiki/Amazon_Elastic_Compute_Cloud).

  6. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 26, 2020. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -19,8 +19,8 @@ We have provided a dummy repo to follow along with [here](https://github.com/rmi
    - [Install Docker](#install-docker)
    - [Cleaning up the Container and Image](#cleaning-up-the-container-and-image)
    - [Previewing the Django-app project Locally](#previewing-the-django-app-project-locally)
    - [Create an AWS Account](#create-an-aws-account)
    - [Create an AWS EC2 Instance](#create-an-aws-ec2-instance)
    - [Creating an AWS Account](#creating-an-aws-account)
    - [Creating an AWS EC2 Instance](#creating-an-aws-ec2-instance)
    - [EC2 Console](#ec2-console)
    - [AMI](#ami)
    - [Security Groups](#security-groups)
  7. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 26, 2020. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -17,7 +17,6 @@ We have provided a dummy repo to follow along with [here](https://github.com/rmi
    - [Previewing the Project](#previewing-the-project)
    - [Previewing the Django-app project with Docker](#previewing-the-django-app-project-with-docker)
    - [Install Docker](#install-docker)
    - [Build and Run the Container](#build-and-run-the-container)
    - [Cleaning up the Container and Image](#cleaning-up-the-container-and-image)
    - [Previewing the Django-app project Locally](#previewing-the-django-app-project-locally)
    - [Create an AWS Account](#create-an-aws-account)
  8. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 26, 2020. 1 changed file with 62 additions and 83 deletions.
    145 changes: 62 additions & 83 deletions deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,6 @@

    Following this tutorial, you will have an application that has:

    - A Django app
    - An AWS EC2 server configured to host your application
    - SSL-certification with Certbot
    - A custom domain name
    @@ -15,12 +14,12 @@ We have provided a dummy repo to follow along with [here](https://github.com/rmi

    ## Table of Contents
    - [Project Layout](#project-layout)
    - [Setting up the project](#setting-up-the-project)
    - [Setting up the Django-app project with Docker](#setting-up-the-django-app-project-with-docker)
    - [Previewing the Project](#previewing-the-project)
    - [Previewing the Django-app project with Docker](#previewing-the-django-app-project-with-docker)
    - [Install Docker](#install-docker)
    - [Build and Run the Container](#build-and-run-the-container)
    - [Cleaning up the Container and Image](#cleaning-up-the-container-and-image)
    - [Setting up the Django-app project manually](#setting-up-the-django-app-project-manually)
    - [Previewing the Django-app project Locally](#previewing-the-django-app-project-locally)
    - [Create an AWS Account](#create-an-aws-account)
    - [Create an AWS EC2 Instance](#create-an-aws-ec2-instance)
    - [EC2 Console](#ec2-console)
    @@ -56,99 +55,101 @@ We have provided a dummy repo to follow along with [here](https://github.com/rmi
    ## Project Layout

    If you already have a working project go ahead and move on to either [Creating your AWS Account](#create-an-aws-account) or [Creating your EC2 Instance](#create-an-aws-ec2-instance).

    Otherwise feel free to use the generic Django project I created on Github [here](https://github.com/rmiyazaki6499/django-app.git).

    Here is the project layout:

    ```
    django-app
    |___ backend/ (Django Backend settings)
    | |___ settings.py
    |___ static_files/
    |___ templates/ (Django Templates)
    | |___ index.html
    |___ manage.py
    |___ requirements.txt
    django-app
    |___ backend/ (Django Backend settings)
    | |___ settings.py
    |___ static_files/
    |___ templates/ (Django Templates)
    | |___ index.html
    |___ manage.py
    |___ requirements.txt
    ```

    ---

    ### _Setting up the project_
    ### Previewing the Project

    I will be using a generic Django project which will run it's own server.
    The app simply displays the default Django app view.
    Start by cloning the project with the command:

    - On your terminal, clone the repository with Git:

    ```
    $ git clone https://github.com/rmiyazaki6499/django-app.git
    ```

    ### Setting up the Django-app project with Docker

    For those that are not interested in setting up the project manually or would simply not have to worry about downloading python and its dependencies, I have created a Dockerfile and docker-compose.yml file to help create a container with everything you would need to run the **django-app**.

    If you would like to build the project manually without running a Docker container, go ahead [here](#setting-up-the-django-app-project-manually)
    ```
    $ git clone https://github.com/rmiyazaki6499/django-app.git
    ```

    #### Install Docker
    ## Previewing the `django-app` project with Docker

    ### Install Docker

    To make this as easy as possible, we will be using *Docker Compose* to creat our container.
    To make this as easy as possible, we will be using *Docker Compose* to creat our container.

    - If you do not have Docker yet, start by downloading it if you are on a Mac or Windows:
    https://www.docker.com/products/docker-desktop
    - If you do not have Docker yet, start by downloading it if you are on a Mac or Windows:
    https://www.docker.com/products/docker-desktop

    - Or if you are on a Linux Distribution follow the directions here:
    https://docs.docker.com/compose/install/
    - Or if you are on a Linux Distribution follow the directions here:
    https://docs.docker.com/compose/install/

    - To confirm you have Docker Compose, open up your terminal and run the command below:
    - To confirm you have Docker Compose, open up your terminal and run the command below:

    ```
    $ docker-compose --version
    docker-compose version 1.26.2, build eefe0d31
    ```
    ```
    $ docker-compose --version
    docker-compose version 1.26.2, build eefe0d31
    ```

    - Go into the project directory to build and run the container with:

    #### Build and Run the Container
    ```
    $ cd mern-app/
    $ docker-compose up --build
    ```

    - Navigate to http://localhost:8000, you should see something like this:

    - Clone the repo to your local machine:
    ![django-default](https://user-images.githubusercontent.com/41876764/87993902-8d27df00-caa0-11ea-8f66-990932b37ca3.png)

    ### Cleaning up the Container and Image

    ```
    $ git clone https://github.com/rmiyazaki6499/django-app.git
    ```
    - To stop the container from running, use `<Ctrl-C>` twice.
    - To close down the container use the command:

    - Go into the project directory to build and run the container with:
    ```
    $ docker-compose down
    ```

    - Then to clean up the container and image which we are no longer using use the command:

    ```
    $ cd django-app/
    $ docker-compose up --build
    ```
    ```
    $ docker system prune -fa
    ```

    Navigate to http://localhost:8000 to view the site on the local server.
    It should look something like this:
    - Confirm that the container and image is no longer there with:

    ![django-default](https://user-images.githubusercontent.com/41876764/87993902-8d27df00-caa0-11ea-8f66-990932b37ca3.png)

    ### Setting up the Django-app project manually
    ```
    $ docker system df -v
    ```

    If you either did not want to use Docker or was curious to build the django-app manually follow the directions below.
    ## Previewing the `django-app` project locally

    - Go into the project directory and make sure you create a virtual environment for your project by either using venv or pipenv:
    To preview the project on your local machine, follow the directions below.

    - Create a virtual environment:

    ```
    $ cd django-app/
    $ python3 -m venv env
    $ source env/bin/activate
    ```

    - To install Python dependencies, make sure you have pip (https://pip.pypa.io/en/stable/installing/)
    and run this command from the root of the project:
    - Install dependencies for Django (make sure you have pip (https://pip.pypa.io/en/stable/installing/)):

    ```
    $ pip3 install -r requirements.txt
    ```

    - Now migrate the database and collect the static files:
    - Migrate the database and collect the static files:

    ```
    $ python3 manage.py makemigrations
    $ python3 manage.py migrate
    @@ -171,28 +172,6 @@ and run this command from the root of the project:
    It should look something like this:

    ![django-default](https://user-images.githubusercontent.com/41876764/87993902-8d27df00-caa0-11ea-8f66-990932b37ca3.png)

    #### Cleaning up the Container and Image

    To stop the container from running, use `<Ctrl-C>` twice.
    To close down the container use the command:

    ```
    $ docker-compose down
    ```
    Then to clean up the container and image which we are no longer using use the command:

    ```
    $ docker system prune -fa
    ```

    Confirm that the container and image is no longer there with:

    ```
    $ docker system df -v
    ```

    [Back to Table of Contents](#table-of-contents)

    ___

  9. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 26, 2020. 1 changed file with 257 additions and 302 deletions.
    559 changes: 257 additions & 302 deletions deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -1,18 +1,26 @@
    # Deploying a Production ready Django App on EC2 with CI/CD
    # Deploying a Production ready Django app on AWS

    In this guide I will go through the steps of setting up your EC2 instance for your Django project and deploy it with CI/CD using Github Actions.

    *Any commands with the "$" at the beginning run on your local machine and any with "%" should be run on your server.*
    In this tutorial, I will be going over to how to deploy a Django app from start to finish using AWS and EC2. Recently, my partner [Tu](https://github.com/tuvo1106) and I launched our app Hygge Homes (a vacation home rental app for searching and booking vacation homes based off Airbnb) and we wanted to share with other developers some of the lessons we learned along the way.

    Following this tutorial, you will have an application that has:

    - A Django app
    - An AWS EC2 server configured to host your application
    - SSL-certification with Certbot
    - A custom domain name
    - Continuous deployment with Github Actions/SSM Agent

    We have provided a dummy repo to follow along with [here](https://github.com/rmiyazaki6499/django-app.git), but feel free to apply it to your own application as necessary.


    ## Table of Contents
    - [Project Layout](#project-layout)
    - [Setting up the project](#setting-up-the-project)
    - [Setting up the Django-app project with Docker](#setting-up-the-django-app-project-with-docker)
    - [Install Docker](#install-docker)
    - [Build and Run the Container](#build-and-run-the-container)
    - [Cleaning up the Container and Image](#cleaning-up-the-container-and-image)
    - [Setting up the Django-app project manually](#setting-up-the-django-app-project-manually)
    - [Setting up the project](#setting-up-the-project)
    - [Setting up the Django-app project with Docker](#setting-up-the-django-app-project-with-docker)
    - [Install Docker](#install-docker)
    - [Build and Run the Container](#build-and-run-the-container)
    - [Cleaning up the Container and Image](#cleaning-up-the-container-and-image)
    - [Setting up the Django-app project manually](#setting-up-the-django-app-project-manually)
    - [Create an AWS Account](#create-an-aws-account)
    - [Create an AWS EC2 Instance](#create-an-aws-ec2-instance)
    - [EC2 Console](#ec2-console)
    @@ -126,152 +134,142 @@ It should look something like this:

    If you either did not want to use Docker or was curious to build the django-app manually follow the directions below.

    - Go into the project directory and make sure you create a virtual environment for your project by either using venv or pipenv:
    ```
    $ cd django-app/
    $ python3 -m venv env
    $ source env/bin/activate
    ```
    - Go into the project directory and make sure you create a virtual environment for your project by either using venv or pipenv:
    ```
    $ cd django-app/
    $ python3 -m venv env
    $ source env/bin/activate
    ```

    - In order to install Python dependencies, make sure you have pip (https://pip.pypa.io/en/stable/installing/)
    - To install Python dependencies, make sure you have pip (https://pip.pypa.io/en/stable/installing/)
    and run this command from the root of the project:

    ```
    $ pip3 install -r requirements.txt
    ```
    ```
    $ pip3 install -r requirements.txt
    ```

    - We will now migrate the database and collect the static files:
    ```
    $ python3 manage.py makemigrations
    $ python3 manage.py migrate
    $ python3 manage.py collectstatic
    ```
    - Now migrate the database and collect the static files:
    ```
    $ python3 manage.py makemigrations
    $ python3 manage.py migrate
    $ python3 manage.py collectstatic
    ```

    - To run the development server, use the following command:
    - Run the development server with the following command:

    ```
    $ python3 manage.py runserver
    ```
    ```
    $ python3 manage.py runserver
    ```

    - To run the production server, use the following command:
    - Run the production server with the following command:

    ```
    $ ENV_PATH=.env-prod python3 manage.py runserver
    ```
    ```
    $ ENV_PATH=.env-prod python3 manage.py runserver
    ```

    Navigate to http://localhost:8000 to view the site on the local server.
    It should look something like this:
    Navigate to http://localhost:8000 to view the site on the local server.
    It should look something like this:

    ![django-default](https://user-images.githubusercontent.com/41876764/87993902-8d27df00-caa0-11ea-8f66-990932b37ca3.png)
    ![django-default](https://user-images.githubusercontent.com/41876764/87993902-8d27df00-caa0-11ea-8f66-990932b37ca3.png)

    #### Cleaning up the Container and Image
    #### Cleaning up the Container and Image

    To stop the container from running, use `<Ctrl-C>` twice.
    To close down the container use the command:
    To stop the container from running, use `<Ctrl-C>` twice.
    To close down the container use the command:

    ```
    $ docker-compose down
    ```
    Then to clean up the container and image which we are no longer using use the command:
    ```
    $ docker-compose down
    ```
    Then to clean up the container and image which we are no longer using use the command:

    ```
    $ docker system prune -fa
    ```
    ```
    $ docker system prune -fa
    ```

    Confirm that the container and image is no longer there with:
    Confirm that the container and image is no longer there with:

    ```
    $ docker system df -v
    ```
    ```
    $ docker system df -v
    ```

    [Back to Table of Contents](#table-of-contents)

    ___

    ## Create an AWS Account

    Before creating an EC2 Instance, you will need an AWS account.
    ## Creating an AWS Account

    If you don't have one already check out this step by step by Amazon to create your AWS account [here](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/).

    Before we move on to create an EC2 instance, make sure that you are making your account as secure as possible by following the prompt on your Security Status checklist.
    Why choose AWS?

    - It offers a lot of free services for new accounts.
    - Very popular among startups and even enterprises.
    - Customer service support, in our opinion, is a step above the competition.
    - If you do not have an account, check out this step by step guide by Amazon [here](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/).

    Before you provision a new server, it is best practice to make sure your account is as secure as possible by following the prompts on your Security Status checklist. This can be found under the IAM tab from your console's homepage.

    ![security_status](https://user-images.githubusercontent.com/41876764/86527279-47d5a180-be52-11ea-97e0-537b62a987b7.png)

    ---

    ## Create an AWS EC2 Instance

    Once you have set up your user account we will jump in to creating our first EC2 Instance.

    *Note: This tutorial assumes you have at least access to the AWS Free Tier products*

    Amazon's **EC2** or *Elastic Compute Cloud* is one of the products/services AWS provides and is one of the main building blocks for many of AWS's services.
    It allows users to rent virtual computers on which to run their own computer applications.

    You can learn more about EC2 [here](https://en.wikipedia.org/wiki/Amazon_Elastic_Compute_Cloud).

    Start out by going into the AWS Console and going to the EC2 console.
    An easy way to get there is through the Services link at the top and searching EC2 in the search prompt.

    *We recommend setting your AWS Region to the one closest to you or your intended audience. However, please note that not all AWS Services will be available depending on the Region.*
    *For our example, we will be working out of the us-east-1 as this Region supports all AWS Services*.
    Amazon's EC2 or Elastic Compute Cloud is one of the core products/services AWS provides and is the main building block for many of AWS's other services. It allows users to essentially rent virtual computers on which to run their own applications. You can learn more about EC2 [here](https://en.wikipedia.org/wiki/Amazon_Elastic_Compute_Cloud).

    Start out by going into the AWS Console and going to the EC2 tab. An easy way to get there is through the Services link at the top and search for EC2 in the prompt.

    *We recommend setting your AWS Region to the one closest to you or your intended audience. However, please note that not all AWS Services will be available depending on the region. For our example, we will be working out of the us-east-1 as this region supports all AWS Services.*

    ---

    ### _EC2 Console_

    You should end up at a screen which looks like this (As of July 2020).
    You should see this screen (as of July 2020):

    ![ec2_console](https://user-images.githubusercontent.com/41876764/86527285-5a4fdb00-be52-11ea-9de2-8ad9dabfd9f3.png)

    Go to the **Running Instances** link on the EC2 Console Dashboard to find yourself at this screen.
    Go to the **Running Instances** link on the EC2 dashboard and click Launch Instance.

    ![ec2_running_instances](https://user-images.githubusercontent.com/41876764/86527322-c8949d80-be52-11ea-9bcb-83ab9a1c8ac6.png)

    ---

    ### _AMI_

    We will then go to **Launch Instance** which will give you several prompts.

    AWS will first ask you to choose an AMI.
    If you do not already have an AMI set up choose an OS that you would like to work in.
    For our example, we will use Ubuntu 18.04 with 64-bit.
    In this step, AWS will prompt you to choose an AMI. AMI's are templates to configure new instances. For this tutorial, we will be using Ubuntu 18.04 64-bit (free tier).

    ![ec2_choose_ami](https://user-images.githubusercontent.com/41876764/86527338-dba76d80-be52-11ea-834b-f0576918cc40.png)

    Next we will choose an instance type.
    We will choose the t2.micro type as it is eligible for the free tier.
    Next, select the **t2.micro** instance type.

    ![ec2_choose_instance_type](https://user-images.githubusercontent.com/41876764/86527344-eb26b680-be52-11ea-8636-3c49552b5872.png)

    Once selected, click forward to **Next: Configure Security Group**.
    On the next screen, keep clicking next until you see the option to **Configure Security Group**.

    ---

    ### _Security Groups_

    **This is important!**
    *Without configuring Security groups the ports on the instance will not be open and therefore your app will not be able to communicate through your instance.*

    Set your Security Group Setting like so:
    Security groups are virtual firewalls for your instances.

    **Important:** *by default, there is an implicit deny on all ports meaning if you do not add rules, all incoming/outgoing traffic is blocked. Security groups are also stateful, which means setting inbound rules for a certain port will also affect the outbound rules for that port.*

    Set your Security Group settings with the following:

    ![django_security_groups](https://user-images.githubusercontent.com/41876764/87994611-45a25280-caa2-11ea-8239-a1e409e4000a.png)

    I will explain each of the ports we will allow as the firewall rules for traffic.
    *Setting Anywhere on Source for Custom TCP will display a warning flag, but you can ignore that for this tutorial. Ideally, you only want to set known IP addresses.*

    | Type | Port Range | Description. |
    | ------------ | :--------: | ------------------------------------------: |
    | SSH | 22 | Port for SSH'ing into your server |
    | HTTP | 80 | Port for HTTP requests to your web server |
    | HTTPS | 443 | Port for HTTPS requests to your web server |
    | Custom TCP | 8000 | Port which Django will run |
    | Custom TCP | 5432 | Database port (Postgres for this example) |
    | Custom TCP | 5432 | Port at which to connect to PostgreSQL |

    **As you can see with the Warning near the bottom that you do not want to set your Source as Anywhere.
    Make sure to set it to your IP address or any IP which will need access to the instance.
    I have this setting so that I do not show my IP address**.
    As you can see with the warning near the bottom of the screen, you do not want to set your *SSH Source IP* as anywhere. This will create a security vulnerability as anyone can try to attempt to log into your server.

    Therefore, be sure to set it to your own IP address and any other IP address which may need access to the instance.

    ---

    @@ -284,46 +282,43 @@ $ docker system df -v

    ### _Key Pairs_

    A key pair consists of a **public key** that AWS stores, and a **private key file** that you store. Together they allow you to connect to your instance securely.

    If this is the first time for you to create a key pair for your project, select **Create a new key pair** from the drop down and add the name of the key pair.
    Once you launch the instance, AWS will prompt you to create a key pair. A key pair consists of a public key that AWS stores and a private key file that you store. Together they allow you to connect to your instance securely through asymmetrical encryption.

    If this is the first time you are creating a key pair for your project, select **Create a new key pair** from the drop-down and add a name for the key pair.

    *Be sure to store the key pair in a secure location. It is generated only once and AWS will not have access to it if you lose it. This is your only means to log into the EC2 instance via SSH.*

    ![key_pair](https://user-images.githubusercontent.com/41876764/86527366-0bef0c00-be53-11ea-9f2c-570e3daa3105.png)

    Once you have downloaded the **key pair** make sure to move the **.pem** file to the root directory of your project.

    Make sure to check the checkbox acknowledging that you have access to the private key pair and click **Launch Instances**.
    Once you have downloaded the **key pair** make sure to move the **.pem** file to the root directory of your project on your local computer.

    This should take you to the **Launch Status** page.
    ![mern-app_root_w_pem](https://user-images.githubusercontent.com/41876764/86527373-17423780-be53-11ea-91fe-fa2a108cc937.png)

    Next, check the checkbox acknowledging that you have access to the private key pair and click Launch Instances. This should take you to the Launch Status page.

    ---

    ## Accessing your EC2 Instance

    Find your way back to the **Instances** page un the EC2 Dashboard.
    It should look something like this:
    Click on the Instances tab on your EC2 console.

    ![ec2_instance_first_initializing](https://user-images.githubusercontent.com/41876764/86527378-21643600-be53-11ea-8323-f92d50ed00a8.png)

    If you go to your instance right after launching the instance may still be initializing.
    After a few minutes, under the **Status Checks** tab, it should show 2/2 checks...
    If it does, congratulations! You have your first EC2 Instance.
    The instance may take a couple of minutes to launch. Once it passes its' status checks, the instance state should show a green circle and a "running" state.

    ---

    ### _Elastic IP_

    Before we access our EC2 Instance it is important to first receive an Elastic IP and Allocate it to our EC2 instance.

    An Elastic IP is a dedicated IP address for your EC2 instance.
    This is important because although our instance does have an IP address assigned out of the box, it does not persist.
    With an Elastic IP address, you can mask the failure of an instance or software by rapidly remapping the address to another instance in your account.

    Therefore by using an Elastic IP we can have a dedicated IP to which users from the internet can access your instance.

    **Note: If you are using the free tier, AWS will charge you unless your EC2 Instance is allocated to an Elastic IP**.

    On the EC2 Dashboard look under the **Network & Security** tab and go to **Elastic IPs**.
    Before you can log into your EC2 instance, it is important to first generate an Elastic IP and associate it to your EC2 instance.

    An Elastic IP is a dedicated IP address for your EC2 instance. Although the instance has a public IP address assigned upon creation, that IP address is dynamic and does not persist if you stop and start the instance. With an Elastic IP address, you can mask the failure of an instance by remapping the address to another instance in your account.

    Therefore, by using an Elastic IP, you can have a dedicated IP to which users from the internet can access your instance. This will come in handy later when you assign a custom domain name and add SSL certification to the server.

    *Note: If you are using the free tier, AWS will charge you if your Elastic IP is NOT associated with an AWS identity.*

    On the EC2 dashboard, look under the **Network & Security** tab and go to **Elastic IPs**:

    ![elastic_ips_link](https://user-images.githubusercontent.com/41876764/86527387-2e812500-be53-11ea-92c6-806ecc97ae2c.png)

    @@ -337,24 +332,31 @@ $ docker system df -v

    ![allocate_ip_address](https://user-images.githubusercontent.com/41876764/86527396-422c8b80-be53-11ea-83a2-8c8b49963bbf.png)

    Go ahead and click **Allocate**.

    This should create an Elastic IP for you.
    Select **Allocate**.

    ![elastic_ip_created](https://user-images.githubusercontent.com/41876764/86527403-4f497a80-be53-11ea-8649-d00eded3a2dc.png)

    Next we must allocate our Elastic IP to our instance.

    With the Elastic IP checked on the left side.
    - Go to **Actions**
    This should create an Elastic IP. The next step is to associate that Elastic IP to the instance.

    With the Elastic IP checked on the left side:

    - Go to Actions
    - Click on **Associate Elastic IP address**
    - Make sure your Resource type is **Instance**
    - Make sure your Resource type is Instance
    - Search for your instance (if this is your first time, it should be the only one)
    - Click **Associate**

    To check if everything is done correctly, go to the Instances tab and in the instance details, you should see the Elastic IP.

    ---

    ### _Connecting to your EC2 Instance_

    Let's check to make sure our Elastic IP is Associated with our instance.
    With the instance selected in the EC2 console, click Connect near the top. You will be prompted with directions on how to connect to your EC2 instance:

    Go to **Instances** and in the instance details you should see **Elastic IP: <your-elastic-ip>**.
    ![connect_to_your_instance](https://user-images.githubusercontent.com/41876764/86527414-5b353c80-be53-11ea-975f-c2f53c3e7de8.png)

    - Changing the .pem file's permission to read-only ensures nobody can modify your private key.

    ---

    @@ -397,12 +399,8 @@ $ docker system df -v

    ## EC2 Environment Setup

    You can think of our EC2 instance like a brand new server. There is nothing besides the Operating System and a few other things AWS has added into our instance.

    ### _Setup Web Server_

    Before we start building our project there are a few things we must install/configure on our empty server.
    We will use the following technologies:
    Once you are logged into your server, we will install all of the project dependencies:
    We will install the following:
    - Django and Python3
    - pip
    - gunicorn
    @@ -414,15 +412,21 @@ $ docker system df -v
    ### _Firewall Setup_

    **Enable Firewall and allow OpenSSH**

    ```
    % sudo ufw enable
    % sudo ufw allow OpenSSH
    % sudo ufw allow 'Nginx Full'
    ```

    Check to make sure we are allowing OpenSSH

    ```
    % sudo ufw status
    ```

    Expected output:

    ```
    To Action From
    -- ------ ----
    @@ -434,61 +438,51 @@ $ docker system df -v

    ---

    ### _Installing Software_
    ## Installing Dependencies

    Updating packages:

    ```
    % sudo apt update
    % sudo apt upgrade
    ```

    Installing Python 3, PostgreSQL, NGINX and Gunicorn:

    ```
    % sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx gunicorn curl
    ```

    ---

    ### _Setting up our project_
    ### _Setting up the Project on the Remote Server_

    - On your terminal, clone the repository with Git:

    ```
    % git clone https://github.com/rmiyazaki6499/django-app.git
    ```

    - Next, make sure you create a virtual environment for your project by either using venv or pipenv:
    ```
    % python3 -m venv env
    % source env/bin/activate
    ```

    - In order to install Python dependencies, make sure you have pip (https://pip.pypa.io/en/stable/installing/)
    and run this command from the root of the repo:
    Recall the steps earlier we did with the dummy project on our local machine. We will repeat that on the EC2 instance.

    ```
    % pip3 install -r requirements.txt
    ```
    ```
    % git clone https://github.com/rmiyazaki6499/django-app.git
    % python3 -m venv env
    % source env/bin/activate
    % pip3 install -r requirements.txt
    % python3 manage.py makemigrations
    % python3 manage.py migrate
    % python3 manage.py collectstatic
    ```

    **Note: Make sure to update your .env file so that your project has the correct Environment Varialbes necessary to run.**
    **Note: Make sure to update your .env file so that your project has the correct Environment Varialbes necessary to run.**

    - We will now migrate the database and collect the static files:
    ```
    % python3 manage.py makemigrations
    % python3 manage.py migrate
    % python3 manage.py collectstatic
    ```

    ---

    ### _Configuring Gunicorn_

    Configure the gunicorn.socket file:
    Create a gunicorn.socket file:

    ```
    % sudo vim /etc/systemd/system/gunicorn.socket
    ```

    Use this code:
    Configure the gunicorn.socket file with:

    ```
    [Unit]
    Description=gunicorn socket
    @@ -500,7 +494,8 @@ and run this command from the root of the repo:
    WantedBy=sockets.target
    ```

    Next we will configure the gunicorn.service file:
    Next configure the gunicorn.service file with:

    ```
    % sudo nano /etc/systemd/system/gunicorn.service
    ```
    @@ -524,12 +519,16 @@ and run this command from the root of the repo:
    [Install]
    WantedBy=multi-user.target
    ```

    Start and enable Gunicorn:

    ```
    % sudo systemctl start gunicorn.socket
    % sudo systemctl enable gunicorn.socket
    ```
    To check the status of gunicorn:

    Check the status of gunicorn with:

    ```
    % sudo systemctl status gunicorn.socket
    ```
    @@ -538,15 +537,16 @@ and run this command from the root of the repo:

    ### _Configuring NGINX_

    We have our project now but...we can't see or access it...
    That is why we will need to configure NGINX, our Web Server so that we can access it.
    Next, we need to configure NGINX to redirect web traffic.

    Create a new NGINX config file with the following command:

    To create a new config file and configure NGINX, use the command: (You can use either nano or vim, I personally use vim):
    ```
    % sudo vim /etc/nginx/sites-available/<YOUR-PROJECT-NAME>
    ```
    Add this into your config file and replace any of the ALL CAPS Sections with your own details:

    Paste in the following configurations and replace any of the ALL CAPS sections with your own project details:

    ```
    server {
    listen 80;
    @@ -568,70 +568,58 @@ server {
    }
    ```

    We will then enable the config file by linking to the sites-enabled directory.
    This is important because otherwise NGINX will simply use the `default` configuration settings located at `/etc/nginx/sites-available/default`.

    ```
    % sudo ln -s /etc/nginx/sites-available/<YOUR-PROJECT-NAME> /etc/nginx/sites-enabled
    ```

    Here is an example for the server name line in the config file:
    *Note: You do not need the http:// portion of the IP*.
    Once your NGINX config is set up, make sure there are no syntax errors with:

    ```
    server_name 18.XXX.XXX.XXX
    % sudo nginx -t
    ```

    Save and exit the file:
    vim: `Shift` + `zz`
    nano: `ctrl` + `x` and selecting `Yes`

    Once your NGINX config is set up.
    Next, create a soft link of your config file from sites-available to the sites-enabled directory. This step is important because NGINX will use the configuration settings located at /etc/nginx/sites-available/default by default if there is nothing in sites-enabled.

    Make sure there are no syntax errors with:
    ```
    % sudo nginx -t
    % sudo ln -s /etc/nginx/sites-available/<YOUR-PROJECT-NAME> /etc/nginx/sites-enabled
    ```

    Restart the NGINX Web Server with:

    ```
    % sudo systemctl restart nginx
    ```

    Now if you go to your Elastic IP on your browser it should show the App!
    [Back to Table of Contents](#table-of-contents)
    Now if you go to your Elastic IP on your browser it should show the app!

    [Back to Table of Contents](#table-of-contents)

    ---

    ## Setting up Continuous Deployment

    Continuous Deployment is helpful because it saves you the time of having to ssh into your EC2 instance each time you make an update on your code base.
    Continuous Deployment is helpful because it saves you the time of having to ssh into your EC2 instance each time you make an update on your codebase.

    We will be using Github Actions and [AWS SSM Send-Command](https://github.com/marketplace/actions/aws-ssm-send-command) created by **peterkimzz** to implement auto deployment.
    In this project, we will be using a Github Action called [AWS SSM Send-Command](https://github.com/marketplace/actions/aws-ssm-send-command) created by **peterkimzz** to implement auto-deployment.

    ### _Activate SSM Agent_
    ### _Github Actions_

    For Github Actions to be able to work, it needs a way for it to communicate to our EC2 Instance when there is a push to the master branch of our repo. There are ways of utilizing, for example webhooks with something like Jenkins to communicate to our server. But for this example, we will use an SSM Agent as a means of connecting Github Actions with our EC2 instance.

    You can think of an SSM Agent as a "back door" to our instance and is something that comes by default to most EC2 instances (I believe Ubuntu and Linux instances have it built in, not sure of the others).
    Even though it is pre-installed, we need to assign an **IAM Role** to our instance to allow it to have access to SSM.
    Github Actions is a service by Github that allows you to perform actions such as run scripts every time something happens to a repository. In our case, we will run a script to install the latest dependencies and restart our server every time a push is made to master.

    For Github Actions to work, it needs a way to communicate with the EC2 Instance and vice-versa. In order to do that, we need to assign permissions via IAM roles.

    ---

    ### _Create SSM Role_

    To create an **IAM Role** with `AmazonSSMFullAccess` permissions:
    - Open the IAM console at https://console.aws.amazon.com/iam/.
    - In the navigation pane, choose **Roles**, and then choose **Create role**.
    - In the navigation panel, select **Roles**, and then click **Create role**.
    - Under *Select type of trusted entity*, choose **AWS service**.
    - In the Choose a use case section, choose **EC2**, and then choose **Next: Permissions**.
    - In the *Choose a use case* section, choose **EC2**, and then choose **Next: Permissions**.
    - On the Attached permissions policy page, search for the `AmazonSSMFullAccess` policy, choose it, and then choose **Next: Review**.
    - On the **Review** page, type a name in the Role name box, and then type a description.
    - Choose **Create role**. The system returns you to the Roles page.

    ---

    ### _Assign SSM Role to EC2 Instance_
    ### _Assigning an SSM Role to EC2 Instance_

    Once you have the **Role** created:
    - Go to the **EC2 Instance Dashboard**
    @@ -643,19 +631,15 @@ server {
    - Select the SSM Role you had created earlier
    - Hit **Apply** to save changes

    With this your EC2 Instance has access to SSM!

    ---

    ### _Github Secrets_

    With our instance being able to use the SSM Agent, we will need to provide it some details so that it can access our EC2 instance.

    This would be provided as Github Secrets. They act like environment variables for our project which is useful because we do not want anyone seeing our Secrets publicly anywhere!

    There are three Secrets we will need: `AWS_ACCESS_KEY`, `AWS_SECRET_ACCESS_KEY`, and `INSTANCE_ID`.

    Before we start, there is an article by AWS on how to find your AWS Access Key and Secret Access Key [here](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys).
    Now that the instance is able to communicate to Github via SSM Agent, you will need to provide the repo with credentials. Github Secrets act like environment variables for repositories and store sensitive data such as AWS login information. In order for the Github Actions script to work, it needs these three secrets: AWS_ACCESS_KEY, AWS_SECRET_ACCESS_KEY, and INSTANCE_ID.

    There is an article by AWS on how to find your AWS Access Key and Secret Access Key [here](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys). Your instance ID is shown on your instances tab under EC2.

    Start by going to your Github project repo:
    - Then go to your **Settings**
    @@ -671,13 +655,10 @@ server {

    ### _Deployment script_

    This step is an extra step mainly to help make things easier although we can do everything in the next step.
    We will create a bash script which we would have the SSM run with the Github Action is triggered.

    Go to your EC2 instance and at the root of your project, create a **.sh** script:
    Next, let's create a bash script to download dependencies and restart NGINX and PM2. Inside the EC2 instance, create a deploy.sh script in the root of the directory:

    ```
    % vim redeploy.sh
    % vim deploy.sh
    ```
    Fill the contents with the bash commands we ran to build the project earlier:
    ```
    @@ -691,33 +672,24 @@ server {
    sudo systemctl restart gunicorn
    ```

    I will walk through step-by-step what we are doing at each command:
    1. `git pull origin master` makes sure we have the most up-to-date code which was triggered by a commit to the `master` branch.
    2. `sudo pip3 install -r requirements.txt` installs any new dependencies for the project
    3. `python3 manage.py makemigrations` creates new migrations based on the changes you have made to your models.
    4. `python3 manage.py migrate` applys and unapplys migrations.
    5. `python3 manage.py collectstatic` collects static files from each of your applications (and any other places you specify) into a single location that can easily be served in production.
    6. `sudo systemctl restart nginx` restarts nginx so that it is serving the most recent static files.
    7. `sudo systemctl restart gunicorn` restarts gunicorn WSGI HTTP Server so that it is serving the most recent static files.

    Now that we have a deployment script we are ready for the last part where we define our **.yml** file!

    ---

    ### _YML File_
    ### _YAML File_

    **AWS SSM Send-Command** requires us to create a **.yml** file to execute.
    *AWS SSM Send-Command* requires a .yml file to execute. At the root of the project, create these two directories:

    Start by going into your EC2 instance and at the root of your project create these two directories:
    ```
    % mkdir -p .github/workflows/
    ```
    This is where our **.yml** file will live.
    Create the file with:

    Create a new YAML file with:

    ```
    % sudo vim .github/workflows/deploy.yml
    ```
    Use the example to fill the contents of the **.yml** file

    Paste in the following:

    ```
    name: Deploy using AWS SSM Send-Command
    @@ -741,55 +713,38 @@ server {
    instance-ids: ${{ secrets.INSTANCE_ID }}
    comment: Deploy the master branch
    working-directory: /home/ubuntu/<YOUR PROJECT DIRECTORY>
    command: /bin/sh ./scripts/redeploy.sh
    command: /bin/sh ./deploy.sh
    ```

    Remember the Github Secrets we set in the repo?
    This is where we use them. The Secrets we set become the values here for `aws-access-key-id`, `aws-secret-access-key`, and `instance-ids` which allows **AWS SSM Send-Command** to access our EC2 instance.

    There are 3 parts of the **.yml** file you want to make sure you change for your project:
    1. The `aws-region` should be the same Region as where you have created your EC2 instance. (If you do not know check the top left of your AWS EC2 Console to confirm the Region you are in).
    2. `working-directory` should be the working directory where you created the `redeploy.sh` script.
    3. `command` should be the directions you would like to run. For our case, we created a simple script so that it does not complicate the `command` line here but you can add as many commands here as long as you are following the **.yml** syntax.

    Once the file is set, go ahead and `git add`, `commit`, and `push` to your repo and the magic should start!

    ---

    ### _Issues with Github Actions_

    I struggled with getting this to work and it took several tries.
    I found that if for whatever reason there are problems with your Github Actions deployment it helps to look through the errors.
    To find the errors go to your Github project repo:
    1. Go to **Actions**
    2. You should see a list of `workflows` or commits you have made since creating the **.yml** file.
    3. Click the most recent one.
    4. Click on the **start** link which will show you each step of the job ran.
    5. Click through to the command with a red `X` and find the errors there.

    If you do have issues feel free to reach out to me or **peterkimzz** by creating an issue [here](https://github.com/peterkimzz/aws-ssm-send-command/issues) if you feel that you have done everything 100% and it still does not work.
    **peterkimzz** was extremely responsive and helpful when I was struggling to get this working (Thank you!).

    [Back to Table of Contents](#table-of-contents)
    The Secrets we provided to the repo earlier comes into use in this script.

    There are 3 parts of the .yml file to configure:

    1. The aws-region should be the same region as where you have created your EC2 instance. (If you do not know, check the top left of your EC2 console to confirm the region you are in).
    2. working-directory should be the directory where you created the deploy.sh script.
    3. command should be the command you would like the SSM agent to run.

    Once this is complete, commit and push the workflow to your repo.

    ---

    ## Setting up your Domain

    This is an extra step if you decided to want to buy a domain and use it for your project (I recommend it!).
    There is great satisfaction in being able to tell people to go to www.your-awesome-site.com and have people see your hard work!

    To get started you would need to first purchase a domain. I most commonly use [Google Domains](https://domains.google/) but another popular domain registrar is [GoDaddy](https://www.godaddy.com/). Whichever registrar you use, make sure you have purchased the domain that you want!

    There are two things we would need to configure to connect our project with our domain:
    - Create records on our domain DNS with our registrar.
    - Configure NGINX on our EC2 instance to recognize our domain.
    So far, users can access the site using the Elastic IP. However, it can be difficult to remember and share so we will configure a custom domain name.

    To get started, you need to first purchase a domain. This can range from $10 to $1,000+s. Amazon has a service called Route53 you can use or you can choose other providers such as [Google Domains](https://domains.google/), [GoDaddy](https://www.godaddy.com/), etc. (we used Google for AlgoAcademy which was $10/year).

    There are two steps you would need to configure to connect the project with a custom domain:

    - Create domain records with DNS registrar
    - Configure NGINX on the EC2 instance to recognize the domain

    ---

    ### _Creating Domain records_

    Let's start with configuring our DNS with records:

    - Go to the **DNS** portion of your registrar.
    - Find where you can create custom resource records.

    @@ -800,86 +755,86 @@ server {
    | @ | A | 1h | YOUR-ELASTIC-IP-ADDRESS |
    | www | CNAME | 1h | your-awesome-site.com |

    Once that is set we are good to move on to configure our Web Server!

    ### _Configuring our Web Server_

    Let's configure our Web Server, in our case NGINX to recognize our domain!

    Start by going to your EC2 Instance and going to our NGINX config file:
    Edit the NGINX config file inside your EC2 instance:

    ```
    % sudo vim /etc/nginx/sites-available/default
    ```
    Update the first section of the config file like so:

    Update the `server:server_name` section of the config file:

    ```
    server {
    server_name <YOUR-ELASTIC-IP> your-awesome-site.com www.your-awesome-site.com;
    ...
    ```
    We are simply adding our root domain and our sub domain (In our case with the prefix www) to our NGINX config.

    Next as we always should do after changing our NGINX config file run:
    Save and restart NGINX:

    ```
    sudo sudo systemctl restart nginx
    ```

    And Voila! You are done!
    **Note: Sometimes the domain change does not happen immediately. From my experience it can happen almost instantaneously to a few hours. If the changes haven't happened after 48 hours, double check your work to see if there are any typos or errors**.
    *DNS changes can take up to 48 hours to update so your results may vary. Once it is complete, going to your custom domain should redirect you to your app.*

    ---

    ## HTTPS

    **SSL** or Secure Sockets Layer allows HTTPS requests to happen. Our current project currently uses HTTP requests which can be dangerous for the potential users of your web app. Therefore I always recommend making sure that you are using HTTPS.

    For details on why HTTPS over HTTP [this article](https://ahrefs.com/blog/what-is-https/) is a pretty good deep dive on why.

    Alright! We will be working with Certbot which is provided by letsencrypt.org which is a non-profit organization which helps create SSL Certificates. They are widely used and best of all, FREE!
    Secure Sockets Layer (SSL) is a standard security technology for establishing an encrypted link between a server and a client. So far, we have been serving web content over HTTP, which can be dangerous as data sent between the server and client is not encrypted. If you are handling user sign-in and need to protect data such as passwords or credit card information, it is always best practice to have SSL certification on your applications.

    In this tutorial, we will be using Certbot by letsencrypt.org, a non-profit organization that provides free SSL Certificates.

    ---

    ### _Installing Certbot_

    On your browser go to https://certbot.eff.org/instructions.

    There select the Software and Operating System (OS) you are using.
    For our example, we are using NGINX and Ubuntu 18.04 LTS (bionic).

    Go to your EC2 Instance and follow the instructions until they ask you to run the command:
    Select the Software and Operating System (OS) you are using. In this case, we are using NGINX and Ubuntu 18.04 LTS (bionic).

    Inside your EC2 Instance, follow the command-line instructions until you see these instructions:

    ```
    % sudo certbot --nginx
    ```
    After running the command certbot will prompt you with several options, the first being:
    `Which names would you like to activate HTTPS for?`
    And if your NGINX config is configured correctly, should show both your root domain as well as with the www subdomain, like so:

    After running this command, Certbot will present to you the following prompt: Which names would you like to activate HTTPS for?

    If NGINX is configured correctly, it should show both your root domain as well as with the www subdomain:

    ```
    1: your-awesome-site.com
    2: www.your-awesome-site.com
    ```
    I usually recommend just hitting `Enter` to activate HTTPS for both because, why not?!

    The next prompt would be:
    ```
    Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    1: No redirect - Make no further changes to the web server configuration.
    2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
    new sites, or if you're confident your site works on HTTPS. You can undo this
    change by editing your web server's configuration.
    ```
    I typically go with `2: Redirect` as it seems to make more sense to have all requests be in HTTPS.
    There are probably situations where it is not the best option but for our case we will go with this one.

    Afterwards, Certbot will go ahead and make a few changes to our NGINX config file.

    Select enter to activate both HTTP and HTTPs. The next prompt will be:

    ```
    Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    1: No redirect - Make no further changes to the web server configuration.
    2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
    new sites, or if you're confident your site works on HTTPS. You can undo this
    change by editing your web server's configuration.
    ```

    Select option 2 as this will redirect all traffic through HTTPS and is the most secure option. Afterward, Certbot will make changes to the NGINX configuration file.

    *Note: Once your site is using HTTPS, double-check your API calls and make sure that they are going to the https:// endpoint rather than http://. This may be an unnecessary precaution, but it is an easy bug to miss.*

    Next, go to your custom domain. Check to see if there is a lock icon next to your URL.

    **Note: Once your site is using Https, make sure to double check your API calls and make sure that they are making calls with https:// rather than http://. This may be an unnecessary precaution but I have had issues with this in the past**.
    ![secure_site](https://user-images.githubusercontent.com/41876764/86527267-2674b580-be52-11ea-9405-874f4f4ba7f0.png)

    After a few moments checkout your domain at `your-awesome-site.com`.
    Congratulations! You have successfully deployed a web app with HTTPS!

    Check to make sure that there is a lock icon next to your site.
    ---

    ![secure_site](https://user-images.githubusercontent.com/41876764/86527267-2674b580-be52-11ea-9405-874f4f4ba7f0.png)
    ## Closing Thoughts

    Congratulations! You have successfully deployed a web app with HTTPS!
    I hope this provided some help for those getting started with web development and AWS. If you run into any problems, please feel free to reach out to either me or [Tu](https://github.com/tuvo1106/tuvo1106) and we can try our best to help. Thank you for reading!

    [Back to Table of Contents](#table-of-contents)
  10. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 15, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -749,7 +749,7 @@ server {

    There are 3 parts of the **.yml** file you want to make sure you change for your project:
    1. The `aws-region` should be the same Region as where you have created your EC2 instance. (If you do not know check the top left of your AWS EC2 Console to confirm the Region you are in).
    2. `working-directory` should be the working directory where you created the `deploy.sh` script.
    2. `working-directory` should be the working directory where you created the `redeploy.sh` script.
    3. `command` should be the directions you would like to run. For our case, we created a simple script so that it does not complicate the `command` line here but you can add as many commands here as long as you are following the **.yml** syntax.

    Once the file is set, go ahead and `git add`, `commit`, and `push` to your repo and the magic should start!
  11. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 15, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -741,7 +741,7 @@ server {
    instance-ids: ${{ secrets.INSTANCE_ID }}
    comment: Deploy the master branch
    working-directory: /home/ubuntu/<YOUR PROJECT DIRECTORY>
    command: /bin/sh ./deploy.sh
    command: /bin/sh ./scripts/redeploy.sh
    ```

    Remember the Github Secrets we set in the repo?
  12. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 15, 2020. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -677,7 +677,7 @@ server {
    Go to your EC2 instance and at the root of your project, create a **.sh** script:

    ```
    % vim deploy.sh
    % vim redeploy.sh
    ```
    Fill the contents with the bash commands we ran to build the project earlier:
    ```
    @@ -688,7 +688,7 @@ server {
    python3 manage.py migrate
    python3 manage.py collectstatic
    sudo systemctl restart nginx
    sudo pm2 restart all
    sudo systemctl restart gunicorn
    ```

    I will walk through step-by-step what we are doing at each command:
    @@ -698,13 +698,13 @@ server {
    4. `python3 manage.py migrate` applys and unapplys migrations.
    5. `python3 manage.py collectstatic` collects static files from each of your applications (and any other places you specify) into a single location that can easily be served in production.
    6. `sudo systemctl restart nginx` restarts nginx so that it is serving the most recent static files.
    7. `sudo pm2 restart all` resets PM2 so that it knows to recognize any changes to the backend (This might be unnecessary because if you were following along, we set PM2 to be `--watch` which automatically recognizes any chances).
    7. `sudo systemctl restart gunicorn` restarts gunicorn WSGI HTTP Server so that it is serving the most recent static files.

    Now that we have a deployment script we are ready for the last part where we define our **.yml** file!

    ---

    ### _yml File_
    ### _YML File_

    **AWS SSM Send-Command** requires us to create a **.yml** file to execute.

  13. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 15, 2020. 1 changed file with 21 additions and 1 deletion.
    22 changes: 21 additions & 1 deletion deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -11,6 +11,7 @@
    - [Setting up the Django-app project with Docker](#setting-up-the-django-app-project-with-docker)
    - [Install Docker](#install-docker)
    - [Build and Run the Container](#build-and-run-the-container)
    - [Cleaning up the Container and Image](#cleaning-up-the-container-and-image)
    - [Setting up the Django-app project manually](#setting-up-the-django-app-project-manually)
    - [Create an AWS Account](#create-an-aws-account)
    - [Create an AWS EC2 Instance](#create-an-aws-ec2-instance)
    @@ -162,7 +163,26 @@ Navigate to http://localhost:8000 to view the site on the local server.
    It should look something like this:

    ![django-default](https://user-images.githubusercontent.com/41876764/87993902-8d27df00-caa0-11ea-8f66-990932b37ca3.png)


    #### Cleaning up the Container and Image

    To stop the container from running, use `<Ctrl-C>` twice.
    To close down the container use the command:

    ```
    $ docker-compose down
    ```
    Then to clean up the container and image which we are no longer using use the command:

    ```
    $ docker system prune -fa
    ```

    Confirm that the container and image is no longer there with:

    ```
    $ docker system df -v
    ```

    [Back to Table of Contents](#table-of-contents)

  14. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 15, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -82,7 +82,7 @@ $ git clone https://github.com/rmiyazaki6499/django-app.git

    For those that are not interested in setting up the project manually or would simply not have to worry about downloading python and its dependencies, I have created a Dockerfile and docker-compose.yml file to help create a container with everything you would need to run the **django-app**.

    If you would like to build the project manually without running a Docker container, go ahead [here]
    If you would like to build the project manually without running a Docker container, go ahead [here](#setting-up-the-django-app-project-manually)

    #### Install Docker

  15. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 15, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -123,7 +123,7 @@ It should look something like this:

    ### Setting up the Django-app project manually

    If you either did not want to use Docker or was curious to build the Django App manually follow the directions below.
    If you either did not want to use Docker or was curious to build the django-app manually follow the directions below.

    - Go into the project directory and make sure you create a virtual environment for your project by either using venv or pipenv:
    ```
  16. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 15, 2020. 1 changed file with 5 additions and 2 deletions.
    7 changes: 5 additions & 2 deletions deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -120,9 +120,12 @@ Navigate to http://localhost:8000 to view the site on the local server.
    It should look something like this:

    ![django-default](https://user-images.githubusercontent.com/41876764/87993902-8d27df00-caa0-11ea-8f66-990932b37ca3.png)


    ### Setting up the Django-app project manually

    If you either did not want to use Docker or was curious to build the Django App manually follow the directions below.

    - Next, go into the project directory and make sure you create a virtual environment for your project by either using venv or pipenv:
    - Go into the project directory and make sure you create a virtual environment for your project by either using venv or pipenv:
    ```
    $ cd django-app/
    $ python3 -m venv env
  17. @rmiyazaki6499 rmiyazaki6499 revised this gist Aug 15, 2020. 1 changed file with 50 additions and 1 deletion.
    51 changes: 50 additions & 1 deletion deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -8,6 +8,10 @@
    ## Table of Contents
    - [Project Layout](#project-layout)
    - [Setting up the project](#setting-up-the-project)
    - [Setting up the Django-app project with Docker](#setting-up-the-django-app-project-with-docker)
    - [Install Docker](#install-docker)
    - [Build and Run the Container](#build-and-run-the-container)
    - [Setting up the Django-app project manually](#setting-up-the-django-app-project-manually)
    - [Create an AWS Account](#create-an-aws-account)
    - [Create an AWS EC2 Instance](#create-an-aws-ec2-instance)
    - [EC2 Console](#ec2-console)
    @@ -74,8 +78,53 @@ django-app
    $ git clone https://github.com/rmiyazaki6499/django-app.git
    ```

    - Next, make sure you create a virtual environment for your project by either using venv or pipenv:
    ### Setting up the Django-app project with Docker

    For those that are not interested in setting up the project manually or would simply not have to worry about downloading python and its dependencies, I have created a Dockerfile and docker-compose.yml file to help create a container with everything you would need to run the **django-app**.

    If you would like to build the project manually without running a Docker container, go ahead [here]

    #### Install Docker

    To make this as easy as possible, we will be using *Docker Compose* to creat our container.

    - If you do not have Docker yet, start by downloading it if you are on a Mac or Windows:
    https://www.docker.com/products/docker-desktop

    - Or if you are on a Linux Distribution follow the directions here:
    https://docs.docker.com/compose/install/

    - To confirm you have Docker Compose, open up your terminal and run the command below:

    ```
    $ docker-compose --version
    docker-compose version 1.26.2, build eefe0d31
    ```

    #### Build and Run the Container

    - Clone the repo to your local machine:

    ```
    $ git clone https://github.com/rmiyazaki6499/django-app.git
    ```

    - Go into the project directory to build and run the container with:

    ```
    $ cd django-app/
    $ docker-compose up --build
    ```

    Navigate to http://localhost:8000 to view the site on the local server.
    It should look something like this:

    ![django-default](https://user-images.githubusercontent.com/41876764/87993902-8d27df00-caa0-11ea-8f66-990932b37ca3.png)


    - Next, go into the project directory and make sure you create a virtual environment for your project by either using venv or pipenv:
    ```
    $ cd django-app/
    $ python3 -m venv env
    $ source env/bin/activate
    ```
  18. @rmiyazaki6499 rmiyazaki6499 revised this gist Jul 20, 2020. 1 changed file with 10 additions and 8 deletions.
    18 changes: 10 additions & 8 deletions deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -19,8 +19,10 @@
    - [Connecting to your EC2 Instance](#connecting-to-your-ec2-instance)
    - [EC2 Environment Setup](#ec2-environment-setup)
    - [Setup Web Server](#setup-web-server)
    - [Firewall Setup](#firewall-setup)
    - [Installing Software](#installing-software)
    - [Setting up our project](#setting-up-our-project)
    - [Starting PM2](#starting-pm2)
    - [Configuring Gunicorn](#configuring-gunicorn)
    - [Configuring NGINX](#configuring-nginx)
    - [Setting up Continuous Deployment](#setting-up-continuous-deployment)
    - [Activate SSM Agent](#activate-ssm-agent)
    @@ -380,29 +382,29 @@ It should look something like this:
    - On your terminal, clone the repository with Git:

    ```
    git clone https://github.com/rmiyazaki6499/django-app.git
    % git clone https://github.com/rmiyazaki6499/django-app.git
    ```

    - Next, make sure you create a virtual environment for your project by either using venv or pipenv:
    ```
    python3 -m venv env
    source env/bin/activate
    % python3 -m venv env
    % source env/bin/activate
    ```

    - In order to install Python dependencies, make sure you have pip (https://pip.pypa.io/en/stable/installing/)
    and run this command from the root of the repo:

    ```
    pip3 install -r requirements.txt
    % pip3 install -r requirements.txt
    ```

    **Note: Make sure to update your .env file so that your project has the correct Environment Varialbes necessary to run.**

    - We will now migrate the database and collect the static files:
    ```
    python3 manage.py makemigrations
    python3 manage.py migrate
    python3 manage.py collectstatic
    % python3 manage.py makemigrations
    % python3 manage.py migrate
    % python3 manage.py collectstatic
    ```

    ---
  19. @rmiyazaki6499 rmiyazaki6499 revised this gist Jul 20, 2020. 1 changed file with 179 additions and 146 deletions.
    325 changes: 179 additions & 146 deletions deploy-django.md
    Original file line number Diff line number Diff line change
    @@ -43,21 +43,21 @@

    If you already have a working project go ahead and move on to either [Creating your AWS Account](#create-an-aws-account) or [Creating your EC2 Instance](#create-an-aws-ec2-instance).

    Otherwise feel free to use the generic MERN project I created on Github [here](https://github.com/rmiyazaki6499/mern-app.git).
    Otherwise feel free to use the generic Django project I created on Github [here](https://github.com/rmiyazaki6499/django-app.git).

    Here is the project layout:

    ```
    django-app
    |__ apps (Django Apps)
    |__ backend/ (Django Backend)
    |__ static_files (Static Files)
    |__ templates (Django Templates)
    |__ static (Collected static files for production)
    |__ requirements.txt (Project dependencies)
    django-app
    |___ backend/ (Django Backend settings)
    | |___ settings.py
    |___ static_files/
    |___ templates/ (Django Templates)
    | |___ index.html
    |___ manage.py
    |___ requirements.txt
    ```


    ---

    @@ -66,55 +66,48 @@
    I will be using a generic Django project which will run it's own server.
    The app simply displays the default Django app view.

    Start by cloning the project with the command:
    ```
    $ git clone https://github.com/rmiyazaki6499/django-app.git
    ```
    Next, we will install dependencies for both Express and React:
    ```
    $ cd mern-app
    $ npm install
    $ cd client
    $ npm install
    ```

    Let's first check to see what our React frontend looks like.
    To run the React server use the command in client directory:
    ```
    $ npm start
    ```
    Now if you go to http://localhost:3000, you should see something like this:

    ![mern-app_react](https://user-images.githubusercontent.com/41876764/87258089-b8358180-c455-11ea-955a-e182e689c993.png)

    The API is not working because well, we are not running our backend yet!
    Let's do that now.

    In another terminal session run the command `npm start` at the root directory of the project as we did with the frontend.
    It should look something like this:

    ![mern-app_run_server](https://user-images.githubusercontent.com/41876764/87258208-ad2f2100-c456-11ea-80c9-7ca9a3624462.png)

    You can see that we have the express server running on port `5000`.

    Now switch back to the http://localhost:3000 and refresh the page. You should see the Message at the bottom be updated!

    ![mern-app_react_success](https://user-images.githubusercontent.com/41876764/87258255-09924080-c457-11ea-97bd-40ad8784a00b.png)

    We have two servers running, one for the React frontend and one for the Express backend.
    If your project set up is essentially two separate projects between the frontend and backend this would be as far as we would need to check.
    However, I have set this project set up so that rather than running two servers, we run a reverse proxy for React through Express and will serve the frontend through the Express server.

    Because we will not be running the React server for our project, go ahead and stop the React server.

    In the `client` directory run the command:
    ```
    $ npm run-script build
    ```

    React then will create a `build` directory with a production build of your app which is where our Express server will use to serve the frontend.

    Now if you go to http://localhost:5000 you should see the same React page from earlier!
    - On your terminal, clone the repository with Git:

    ```
    $ git clone https://github.com/rmiyazaki6499/django-app.git
    ```

    - Next, make sure you create a virtual environment for your project by either using venv or pipenv:
    ```
    $ python3 -m venv env
    $ source env/bin/activate
    ```

    - In order to install Python dependencies, make sure you have pip (https://pip.pypa.io/en/stable/installing/)
    and run this command from the root of the project:

    ```
    $ pip3 install -r requirements.txt
    ```

    - We will now migrate the database and collect the static files:
    ```
    $ python3 manage.py makemigrations
    $ python3 manage.py migrate
    $ python3 manage.py collectstatic
    ```

    - To run the development server, use the following command:

    ```
    $ python3 manage.py runserver
    ```

    - To run the production server, use the following command:

    ```
    $ ENV_PATH=.env-prod python3 manage.py runserver
    ```

    Navigate to http://localhost:8000 to view the site on the local server.
    It should look something like this:

    ![django-default](https://user-images.githubusercontent.com/41876764/87993902-8d27df00-caa0-11ea-8f66-990932b37ca3.png)


    [Back to Table of Contents](#table-of-contents)
    @@ -190,7 +183,7 @@

    Set your Security Group Setting like so:

    ![ec2_security_group_configuration](https://user-images.githubusercontent.com/41876764/87123341-674a4100-c23b-11ea-9216-eb3f7a65f852.png)
    ![django_security_groups](https://user-images.githubusercontent.com/41876764/87994611-45a25280-caa2-11ea-8239-a1e409e4000a.png)

    I will explain each of the ports we will allow as the firewall rules for traffic.

    @@ -199,8 +192,8 @@
    | SSH | 22 | Port for SSH'ing into your server |
    | HTTP | 80 | Port for HTTP requests to your web server |
    | HTTPS | 443 | Port for HTTPS requests to your web server |
    | Custom TCP | 5000 | Port which Express will run |
    | Custom TCP | 27017 | Port at which to connect to MongoDB |
    | Custom TCP | 8000 | Port which Django will run |
    | Custom TCP | 5432 | Database port (Postgres for this example) |

    **As you can see with the Warning near the bottom that you do not want to set your Source as Anywhere.
    Make sure to set it to your IP address or any IP which will need access to the instance.
    @@ -225,8 +218,6 @@

    Once you have downloaded the **key pair** make sure to move the **.pem** file to the root directory of your project.

    ![mern-app_root_w_pem](https://user-images.githubusercontent.com/41876764/86527373-17423780-be53-11ea-91fe-fa2a108cc937.png)

    Make sure to check the checkbox acknowledging that you have access to the private key pair and click **Launch Instances**.

    This should take you to the **Launch Status** page.
    @@ -338,76 +329,136 @@

    Before we start building our project there are a few things we must install/configure on our empty server.
    We will use the following technologies:
    - Node.js 10.x & NPM
    - MongoDB 4.0
    - PM2
    - Django and Python3
    - pip
    - gunicorn
    - NGINX
    - UFW (Firewall)

    There is a script that will install and configure almost everything for you on an Ubuntu 18.04 server courtesy of Jason Watmore.
    *If you would like to better understand what is going on in this script please check out his blog [here](https://jasonwatmore.com/post/2018/09/26/setup-nodejs-mongodb-production-server-on-ubuntu-1804).
    ```
    curl https://gist.github.com/cornflourblue/f0abd30f47d96d6ff127fe8a9e5bbd9f/raw/e3047c9dc3ce8b796e7354c92d2c47ce61981d2f/setup-nodejs-mongodb-production-server-on-ubuntu-1804.sh | sudo bash
    ```

    ---

    ### _Setting up our project_

    Let's begin to build our project.
    ### _Firewall Setup_

    We will first start by cloning our project into the EC2 instance with:
    **Enable Firewall and allow OpenSSH**
    ```
    % sudo ufw enable
    % sudo ufw allow OpenSSH
    % sudo ufw allow 'Nginx Full'
    ```
    Check to make sure we are allowing OpenSSH
    ```
    % git clone https://github.com/rmiyazaki6499/mern-app.git
    % sudo ufw status
    ```
    ```
    To Action From
    -- ------ ----
    Nginx Full ALLOW Anywhere
    OpenSSH ALLOW Anywhere
    Nginx Full (v6) ALLOW Anywhere (v6)
    OpenSSH (v6) ALLOW Anywhere (v6)
    ```

    ---

    We will then start building the project as we would locally.
    ### _Installing Software_

    Installing dependencies for Express:
    Updating packages:
    ```
    % cd mern-app
    % npm install
    % sudo apt update
    % sudo apt upgrade
    ```
    Installing dependencies for React and building our project:

    Installing Python 3, PostgreSQL, NGINX and Gunicorn:
    ```
    % cd client/
    % npm install
    % npm run-script build (or npm build if you have that set up)
    % sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx gunicorn curl
    ```
    This will then create the `build/` directory which will contain the static files Express will use for the frontend.

    ---

    ### _Starting PM2_
    ### _Setting up our project_

    - On your terminal, clone the repository with Git:

    ```
    git clone https://github.com/rmiyazaki6499/django-app.git
    ```

    - Next, make sure you create a virtual environment for your project by either using venv or pipenv:
    ```
    python3 -m venv env
    source env/bin/activate
    ```

    - In order to install Python dependencies, make sure you have pip (https://pip.pypa.io/en/stable/installing/)
    and run this command from the root of the repo:

    ```
    pip3 install -r requirements.txt
    ```

    **Note: Make sure to update your .env file so that your project has the correct Environment Varialbes necessary to run.**

    - We will now migrate the database and collect the static files:
    ```
    python3 manage.py makemigrations
    python3 manage.py migrate
    python3 manage.py collectstatic
    ```

    ---

    PM2 is an Advanced process manager for production Node.js applications which includes functionality like Load Balancing, etc. If we had an API it would run the backend API as well as make sure our processes are running even in case something happens and our app crashes.
    ### _Configuring Gunicorn_

    Our previous script already downloaded and started PM2 for us but we will need it to run our backend as well as add some options for better performance.
    Configure the gunicorn.socket file:
    ```
    % sudo vim /etc/systemd/system/gunicorn.socket
    ```

    To take a look at our current PM2 processes, using the command:
    Use this code:
    ```
    % sudo pm2 status
    [Unit]
    Description=gunicorn socket
    [Socket]
    ListenStream=/run/gunicorn.sock
    [Install]
    WantedBy=sockets.target
    ```

    Next we will configure the gunicorn.service file:
    ```
    You can see that we do not have any processes running yet.
    At the root of your project directory with our Express app run:
    % sudo nano /etc/systemd/system/gunicorn.service
    ```
    % sudo pm2 start app.js
    ```
    *Note: We are using app.js for our app but yours may use server.js*.
    Use the configurations below:
    [Unit]
    Description=gunicorn daemon
    Requires=gunicorn.socket
    After=network.target
    [Service]
    User=djangoadmin
    Group=www-data
    WorkingDirectory=/home/ubuntu/<YOUR-PROJECT>
    ExecStart=/home/djangoadmin/pyapps/venv/bin/gunicorn \
    --access-logfile - \
    --workers 3 \
    --bind unix:/run/gunicorn.sock \
    <YOUR-PROJECT>.wsgi:application
    Great! Our PM2 is running our backend, but let's stop it for just a second.
    [Install]
    WantedBy=multi-user.target
    ```
    % sudo pm2 stop app.js
    Start and enable Gunicorn:
    ```
    Let's re-run the process with this command:
    % sudo systemctl start gunicorn.socket
    % sudo systemctl enable gunicorn.socket
    ```
    % sudo pm2 start app.js -i max --watch
    To check the status of gunicorn:
    ```
    % sudo systemctl status gunicorn.socket
    ```
    What difference did this make?
    The `-i max` option allows us to run processes with the max number of threads available.
    Because Node is single threaded, using all available cores will maximize performance.
    The `--watch` option allows the app to automatically restart if there are any changes to the directory.
    You can think of it as similar to the package `nodemon` but for production.

    ---

    @@ -417,37 +468,29 @@

    That is why we will need to configure NGINX, our Web Server so that we can access it.

    Something we have to understand before we proceed is that because our project uses two different frameworks. Which essentially means we have two different projects. Therefore there is a need for us to configure NGINX so that depending on route it knows to direct the request to either the frontend (React) or the backend (Express) portion of the app.

    *For my generic project, I did not create a backend API endpoint but may in the future reiterate this point.*

    To create a new config file and configure NGINX, use the command: (You can use either nano or vim, I personally use vim):
    ```
    % sudo vim /etc/nginx/sites-available/<YOUR-PROJECT-NAME>
    ```
    Add this into your config file and replace any of the ALL CAPS Sections with your own details:
    ```
    server {
    server_name <YOUR EC2 ELASTIC IP ADDRESS>;
    server {
    listen 80;
    server_name YOUR_INSTANCE_IP_ADDRESS;
    # react app & front-end files
    location / {
    root /home/ubuntu/<YOUR PROJECT DIRECTORY>/client/build/;
    try_files $uri /index.html;
    }
    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
    root /home/ubuntu/<YOUR-PROJECT>;
    }
    location /media/ {
    root /home/ubuntu/<YOUR-PROJECT>;
    }
    # node api reverse proxy // the /api/ is assuming your api routes start with that i.e. www.your-site.com/api/endpoint
    location /api/ {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_http_version 1.1;
    proxy_pass http://localhost:5000;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    }
    location / {
    include proxy_params;
    proxy_pass http://unix:/run/gunicorn.sock;
    }
    }
    ```

    @@ -456,16 +499,7 @@

    ```
    % sudo ln -s /etc/nginx/sites-available/<YOUR-PROJECT-NAME> /etc/nginx/sites-enabled
    ```

    A couple of important points.
    The `root` line in the Frontend section needs to be the build directory of your **React** app.
    So for our case it would be `home/ubuntu/mern-app/client/build/`.
    Make sure the path is correct as this will be where your static files will be served.

    The `proxy_pass` in the backend is the route of where your Express will be running.

    *Note:For the **mern-app** project this should be http://localhost:5000 but this can a different port depending on your configurations*.
    ```

    Here is an example for the server name line in the config file:
    *Note: You do not need the http:// portion of the IP*.
    @@ -575,21 +609,20 @@
    ```
    #!/bin/sh
    sudo git pull origin master
    sudo npm install
    cd client
    npm install
    sudo npm run-script build
    cd ..
    sudo pip3 install -r requirements.txt
    python3 manage.py makemigrations
    python3 manage.py migrate
    python3 manage.py collectstatic
    sudo systemctl restart nginx
    sudo pm2 restart all
    ```

    I will walk through step-by-step what we are doing at each command:
    1. `git pull origin master` makes sure we have the most up-to-date code which was triggered by a commit to the `master` branch.
    2. `npm install` installs any new dependencies for the Express side of the app.
    3. `cd client` we move into the `client` directory where our React app lives.
    4. `npm install` install any new dependencies for the React side of the app.
    5. `cd ..` return to the root of the project.
    2. `sudo pip3 install -r requirements.txt` installs any new dependencies for the project
    3. `python3 manage.py makemigrations` creates new migrations based on the changes you have made to your models.
    4. `python3 manage.py migrate` applys and unapplys migrations.
    5. `python3 manage.py collectstatic` collects static files from each of your applications (and any other places you specify) into a single location that can easily be served in production.
    6. `sudo systemctl restart nginx` restarts nginx so that it is serving the most recent static files.
    7. `sudo pm2 restart all` resets PM2 so that it knows to recognize any changes to the backend (This might be unnecessary because if you were following along, we set PM2 to be `--watch` which automatically recognizes any chances).

  20. @rmiyazaki6499 rmiyazaki6499 renamed this gist Jul 20, 2020. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  21. @rmiyazaki6499 rmiyazaki6499 renamed this gist Jul 20, 2020. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  22. @rmiyazaki6499 rmiyazaki6499 created this gist Jul 20, 2020.
    778 changes: 778 additions & 0 deletions deploy-mern.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,778 @@
    # Deploying a Production ready Django App on EC2 with CI/CD

    In this guide I will go through the steps of setting up your EC2 instance for your Django project and deploy it with CI/CD using Github Actions.

    *Any commands with the "$" at the beginning run on your local machine and any with "%" should be run on your server.*


    ## Table of Contents
    - [Project Layout](#project-layout)
    - [Setting up the project](#setting-up-the-project)
    - [Create an AWS Account](#create-an-aws-account)
    - [Create an AWS EC2 Instance](#create-an-aws-ec2-instance)
    - [EC2 Console](#ec2-console)
    - [AMI](#ami)
    - [Security Groups](#security-groups)
    - [Instance Details](#instance-details)
    - [Key Pairs](#key-pairs)
    - [Elastic IP](#elastic-ip)
    - [Connecting to your EC2 Instance](#connecting-to-your-ec2-instance)
    - [EC2 Environment Setup](#ec2-environment-setup)
    - [Setup Web Server](#setup-web-server)
    - [Setting up our project](#setting-up-our-project)
    - [Starting PM2](#starting-pm2)
    - [Configuring NGINX](#configuring-nginx)
    - [Setting up Continuous Deployment](#setting-up-continuous-deployment)
    - [Activate SSM Agent](#activate-ssm-agent)
    - [Create SSM Role](#create-ssm-role)
    - [Assign SSM Role to EC2 Instance](#assign-ssm-role-to-ec2-instance)
    - [Github Secrets](#github-secrets)
    - [Deployment Script](#deployment-script)
    - [yml File](#yml-file)
    - [Issues with Github Actions](#issues-with-github-actions)
    - [Setting up your Domain](#setting-up-your-domain)
    - [Creating Domain records](#creating-domain-records)
    - [Configuring our Web Server](#configuring-our-web-server)
    - [HTTPS](#https)
    - [Installing Certbot](#installing-certbot)


    ---
    ## Project Layout

    If you already have a working project go ahead and move on to either [Creating your AWS Account](#create-an-aws-account) or [Creating your EC2 Instance](#create-an-aws-ec2-instance).

    Otherwise feel free to use the generic MERN project I created on Github [here](https://github.com/rmiyazaki6499/mern-app.git).

    Here is the project layout:

    ```
    django-app
    |__ apps (Django Apps)
    |__ backend/ (Django Backend)
    |__ static_files (Static Files)
    |__ templates (Django Templates)
    |__ static (Collected static files for production)
    |__ requirements.txt (Project dependencies)
    ```


    ---

    ### _Setting up the project_

    I will be using a generic Django project which will run it's own server.
    The app simply displays the default Django app view.

    Start by cloning the project with the command:
    ```
    $ git clone https://github.com/rmiyazaki6499/django-app.git
    ```
    Next, we will install dependencies for both Express and React:
    ```
    $ cd mern-app
    $ npm install
    $ cd client
    $ npm install
    ```

    Let's first check to see what our React frontend looks like.
    To run the React server use the command in client directory:
    ```
    $ npm start
    ```
    Now if you go to http://localhost:3000, you should see something like this:

    ![mern-app_react](https://user-images.githubusercontent.com/41876764/87258089-b8358180-c455-11ea-955a-e182e689c993.png)

    The API is not working because well, we are not running our backend yet!
    Let's do that now.

    In another terminal session run the command `npm start` at the root directory of the project as we did with the frontend.
    It should look something like this:

    ![mern-app_run_server](https://user-images.githubusercontent.com/41876764/87258208-ad2f2100-c456-11ea-80c9-7ca9a3624462.png)

    You can see that we have the express server running on port `5000`.

    Now switch back to the http://localhost:3000 and refresh the page. You should see the Message at the bottom be updated!

    ![mern-app_react_success](https://user-images.githubusercontent.com/41876764/87258255-09924080-c457-11ea-97bd-40ad8784a00b.png)

    We have two servers running, one for the React frontend and one for the Express backend.
    If your project set up is essentially two separate projects between the frontend and backend this would be as far as we would need to check.
    However, I have set this project set up so that rather than running two servers, we run a reverse proxy for React through Express and will serve the frontend through the Express server.

    Because we will not be running the React server for our project, go ahead and stop the React server.

    In the `client` directory run the command:
    ```
    $ npm run-script build
    ```

    React then will create a `build` directory with a production build of your app which is where our Express server will use to serve the frontend.

    Now if you go to http://localhost:5000 you should see the same React page from earlier!


    [Back to Table of Contents](#table-of-contents)

    ___

    ## Create an AWS Account

    Before creating an EC2 Instance, you will need an AWS account.

    If you don't have one already check out this step by step by Amazon to create your AWS account [here](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/).

    Before we move on to create an EC2 instance, make sure that you are making your account as secure as possible by following the prompt on your Security Status checklist.

    ![security_status](https://user-images.githubusercontent.com/41876764/86527279-47d5a180-be52-11ea-97e0-537b62a987b7.png)

    ---

    ## Create an AWS EC2 Instance

    Once you have set up your user account we will jump in to creating our first EC2 Instance.

    *Note: This tutorial assumes you have at least access to the AWS Free Tier products*

    Amazon's **EC2** or *Elastic Compute Cloud* is one of the products/services AWS provides and is one of the main building blocks for many of AWS's services.
    It allows users to rent virtual computers on which to run their own computer applications.

    You can learn more about EC2 [here](https://en.wikipedia.org/wiki/Amazon_Elastic_Compute_Cloud).

    Start out by going into the AWS Console and going to the EC2 console.
    An easy way to get there is through the Services link at the top and searching EC2 in the search prompt.

    *We recommend setting your AWS Region to the one closest to you or your intended audience. However, please note that not all AWS Services will be available depending on the Region.*
    *For our example, we will be working out of the us-east-1 as this Region supports all AWS Services*.

    ---

    ### _EC2 Console_

    You should end up at a screen which looks like this (As of July 2020).

    ![ec2_console](https://user-images.githubusercontent.com/41876764/86527285-5a4fdb00-be52-11ea-9de2-8ad9dabfd9f3.png)

    Go to the **Running Instances** link on the EC2 Console Dashboard to find yourself at this screen.

    ![ec2_running_instances](https://user-images.githubusercontent.com/41876764/86527322-c8949d80-be52-11ea-9bcb-83ab9a1c8ac6.png)

    ---

    ### _AMI_

    We will then go to **Launch Instance** which will give you several prompts.

    AWS will first ask you to choose an AMI.
    If you do not already have an AMI set up choose an OS that you would like to work in.
    For our example, we will use Ubuntu 18.04 with 64-bit.

    ![ec2_choose_ami](https://user-images.githubusercontent.com/41876764/86527338-dba76d80-be52-11ea-834b-f0576918cc40.png)

    Next we will choose an instance type.
    We will choose the t2.micro type as it is eligible for the free tier.

    ![ec2_choose_instance_type](https://user-images.githubusercontent.com/41876764/86527344-eb26b680-be52-11ea-8636-3c49552b5872.png)

    Once selected, click forward to **Next: Configure Security Group**.

    ---

    ### _Security Groups_

    **This is important!**
    *Without configuring Security groups the ports on the instance will not be open and therefore your app will not be able to communicate through your instance.*

    Set your Security Group Setting like so:

    ![ec2_security_group_configuration](https://user-images.githubusercontent.com/41876764/87123341-674a4100-c23b-11ea-9216-eb3f7a65f852.png)

    I will explain each of the ports we will allow as the firewall rules for traffic.

    | Type | Port Range | Description. |
    | ------------ | :--------: | ------------------------------------------: |
    | SSH | 22 | Port for SSH'ing into your server |
    | HTTP | 80 | Port for HTTP requests to your web server |
    | HTTPS | 443 | Port for HTTPS requests to your web server |
    | Custom TCP | 5000 | Port which Express will run |
    | Custom TCP | 27017 | Port at which to connect to MongoDB |

    **As you can see with the Warning near the bottom that you do not want to set your Source as Anywhere.
    Make sure to set it to your IP address or any IP which will need access to the instance.
    I have this setting so that I do not show my IP address**.

    ---

    ### _Instance Details_

    Click forward to **Review and Launch** to view all configurations of your Instance/AMI.
    If the configurations look correct go ahead and hit **Launch**.

    ---

    ### _Key Pairs_

    A key pair consists of a **public key** that AWS stores, and a **private key file** that you store. Together they allow you to connect to your instance securely.

    If this is the first time for you to create a key pair for your project, select **Create a new key pair** from the drop down and add the name of the key pair.

    ![key_pair](https://user-images.githubusercontent.com/41876764/86527366-0bef0c00-be53-11ea-9f2c-570e3daa3105.png)

    Once you have downloaded the **key pair** make sure to move the **.pem** file to the root directory of your project.

    ![mern-app_root_w_pem](https://user-images.githubusercontent.com/41876764/86527373-17423780-be53-11ea-91fe-fa2a108cc937.png)

    Make sure to check the checkbox acknowledging that you have access to the private key pair and click **Launch Instances**.

    This should take you to the **Launch Status** page.

    ---

    ## Accessing your EC2 Instance

    Find your way back to the **Instances** page un the EC2 Dashboard.
    It should look something like this:

    ![ec2_instance_first_initializing](https://user-images.githubusercontent.com/41876764/86527378-21643600-be53-11ea-8323-f92d50ed00a8.png)

    If you go to your instance right after launching the instance may still be initializing.
    After a few minutes, under the **Status Checks** tab, it should show 2/2 checks...
    If it does, congratulations! You have your first EC2 Instance.

    ---

    ### _Elastic IP_

    Before we access our EC2 Instance it is important to first receive an Elastic IP and Allocate it to our EC2 instance.

    An Elastic IP is a dedicated IP address for your EC2 instance.
    This is important because although our instance does have an IP address assigned out of the box, it does not persist.
    With an Elastic IP address, you can mask the failure of an instance or software by rapidly remapping the address to another instance in your account.

    Therefore by using an Elastic IP we can have a dedicated IP to which users from the internet can access your instance.

    **Note: If you are using the free tier, AWS will charge you unless your EC2 Instance is allocated to an Elastic IP**.

    On the EC2 Dashboard look under the **Network & Security** tab and go to **Elastic IPs**.

    ![elastic_ips_link](https://user-images.githubusercontent.com/41876764/86527387-2e812500-be53-11ea-92c6-806ecc97ae2c.png)

    It should take you here:

    ![elastic_ip_addresses](https://user-images.githubusercontent.com/41876764/86527390-393bba00-be53-11ea-8c83-4496e091e78c.png)

    Click on **Allocate Elastic IP address**.

    It should take you here:

    ![allocate_ip_address](https://user-images.githubusercontent.com/41876764/86527396-422c8b80-be53-11ea-83a2-8c8b49963bbf.png)

    Go ahead and click **Allocate**.

    This should create an Elastic IP for you.

    ![elastic_ip_created](https://user-images.githubusercontent.com/41876764/86527403-4f497a80-be53-11ea-8649-d00eded3a2dc.png)

    Next we must allocate our Elastic IP to our instance.

    With the Elastic IP checked on the left side.
    - Go to **Actions**
    - Click on **Associate Elastic IP address**
    - Make sure your Resource type is **Instance**
    - Search for your instance (if this is your first time, it should be the only one)
    - Click **Associate**

    Let's check to make sure our Elastic IP is Associated with our instance.

    Go to **Instances** and in the instance details you should see **Elastic IP: <your-elastic-ip>**.

    ---

    ### _Connecting to your EC2 Instance_

    Now that we have our instance and have allocated an Elastic IP to it.
    It is time to connect to our server!

    If you have not already, go to the **Instances** link in the EC2 Dashboard.

    With the instance highlighted, click on **Connect** on the top banner of the Instaces Dashboard.

    It should give you a pop up with directions on how to connect to your EC2 instance.

    ![connect_to_your_instance](https://user-images.githubusercontent.com/41876764/86527414-5b353c80-be53-11ea-975f-c2f53c3e7de8.png)

    Go back to your project root directory and make sure that your **.pem** file has the correct permissions.

    Run the command:
    ```
    $ chmod 400 *.pem
    ```

    Next run the command given to you in the example:
    ```
    $ ssh -i "<KEY-PAIR>.pem" ubuntu@<YOUR-IP-ADDRESS>.compute-1.amazonaws.com
    ```

    The ssh should prompt you that the authenticity of host *instance* can't be established and will show an ECDSA key fingerprint.
    It will also ask you `Are you sure you want to continue connecting (yes/no)?`

    Type `yes` and *Enter*.

    This should take you into the EC2 Instance.
    If not, try the `ssh` command again.

    Congratulations you are inside your EC2 Instance!

    ---

    ## EC2 Environment Setup

    You can think of our EC2 instance like a brand new server. There is nothing besides the Operating System and a few other things AWS has added into our instance.

    ### _Setup Web Server_

    Before we start building our project there are a few things we must install/configure on our empty server.
    We will use the following technologies:
    - Node.js 10.x & NPM
    - MongoDB 4.0
    - PM2
    - NGINX
    - UFW (Firewall)

    There is a script that will install and configure almost everything for you on an Ubuntu 18.04 server courtesy of Jason Watmore.
    *If you would like to better understand what is going on in this script please check out his blog [here](https://jasonwatmore.com/post/2018/09/26/setup-nodejs-mongodb-production-server-on-ubuntu-1804).
    ```
    curl https://gist.github.com/cornflourblue/f0abd30f47d96d6ff127fe8a9e5bbd9f/raw/e3047c9dc3ce8b796e7354c92d2c47ce61981d2f/setup-nodejs-mongodb-production-server-on-ubuntu-1804.sh | sudo bash
    ```

    ---

    ### _Setting up our project_

    Let's begin to build our project.

    We will first start by cloning our project into the EC2 instance with:
    ```
    % git clone https://github.com/rmiyazaki6499/mern-app.git
    ```

    We will then start building the project as we would locally.

    Installing dependencies for Express:
    ```
    % cd mern-app
    % npm install
    ```
    Installing dependencies for React and building our project:
    ```
    % cd client/
    % npm install
    % npm run-script build (or npm build if you have that set up)
    ```
    This will then create the `build/` directory which will contain the static files Express will use for the frontend.

    ---

    ### _Starting PM2_

    PM2 is an Advanced process manager for production Node.js applications which includes functionality like Load Balancing, etc. If we had an API it would run the backend API as well as make sure our processes are running even in case something happens and our app crashes.

    Our previous script already downloaded and started PM2 for us but we will need it to run our backend as well as add some options for better performance.

    To take a look at our current PM2 processes, using the command:
    ```
    % sudo pm2 status
    ```
    You can see that we do not have any processes running yet.
    At the root of your project directory with our Express app run:
    ```
    % sudo pm2 start app.js
    ```
    *Note: We are using app.js for our app but yours may use server.js*.

    Great! Our PM2 is running our backend, but let's stop it for just a second.
    ```
    % sudo pm2 stop app.js
    ```
    Let's re-run the process with this command:
    ```
    % sudo pm2 start app.js -i max --watch
    ```
    What difference did this make?
    The `-i max` option allows us to run processes with the max number of threads available.
    Because Node is single threaded, using all available cores will maximize performance.
    The `--watch` option allows the app to automatically restart if there are any changes to the directory.
    You can think of it as similar to the package `nodemon` but for production.

    ---

    ### _Configuring NGINX_

    We have our project now but...we can't see or access it...

    That is why we will need to configure NGINX, our Web Server so that we can access it.

    Something we have to understand before we proceed is that because our project uses two different frameworks. Which essentially means we have two different projects. Therefore there is a need for us to configure NGINX so that depending on route it knows to direct the request to either the frontend (React) or the backend (Express) portion of the app.

    *For my generic project, I did not create a backend API endpoint but may in the future reiterate this point.*

    To create a new config file and configure NGINX, use the command: (You can use either nano or vim, I personally use vim):
    ```
    % sudo vim /etc/nginx/sites-available/<YOUR-PROJECT-NAME>
    ```
    Add this into your config file and replace any of the ALL CAPS Sections with your own details:
    ```
    server {
    server_name <YOUR EC2 ELASTIC IP ADDRESS>;
    # react app & front-end files
    location / {
    root /home/ubuntu/<YOUR PROJECT DIRECTORY>/client/build/;
    try_files $uri /index.html;
    }
    # node api reverse proxy // the /api/ is assuming your api routes start with that i.e. www.your-site.com/api/endpoint
    location /api/ {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_http_version 1.1;
    proxy_pass http://localhost:5000;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
    }
    }
    ```

    We will then enable the config file by linking to the sites-enabled directory.
    This is important because otherwise NGINX will simply use the `default` configuration settings located at `/etc/nginx/sites-available/default`.

    ```
    % sudo ln -s /etc/nginx/sites-available/<YOUR-PROJECT-NAME> /etc/nginx/sites-enabled
    ```

    A couple of important points.
    The `root` line in the Frontend section needs to be the build directory of your **React** app.
    So for our case it would be `home/ubuntu/mern-app/client/build/`.
    Make sure the path is correct as this will be where your static files will be served.

    The `proxy_pass` in the backend is the route of where your Express will be running.

    *Note:For the **mern-app** project this should be http://localhost:5000 but this can a different port depending on your configurations*.

    Here is an example for the server name line in the config file:
    *Note: You do not need the http:// portion of the IP*.
    ```
    server_name 18.XXX.XXX.XXX
    ```

    Save and exit the file:
    vim: `Shift` + `zz`
    nano: `ctrl` + `x` and selecting `Yes`

    Once your NGINX config is set up.

    Make sure there are no syntax errors with:
    ```
    % sudo nginx -t
    ```

    Restart the NGINX Web Server with:
    ```
    % sudo systemctl restart nginx
    ```

    Now if you go to your Elastic IP on your browser it should show the App!

    [Back to Table of Contents](#table-of-contents)

    ---

    ## Setting up Continuous Deployment

    Continuous Deployment is helpful because it saves you the time of having to ssh into your EC2 instance each time you make an update on your code base.

    We will be using Github Actions and [AWS SSM Send-Command](https://github.com/marketplace/actions/aws-ssm-send-command) created by **peterkimzz** to implement auto deployment.

    ### _Activate SSM Agent_

    For Github Actions to be able to work, it needs a way for it to communicate to our EC2 Instance when there is a push to the master branch of our repo. There are ways of utilizing, for example webhooks with something like Jenkins to communicate to our server. But for this example, we will use an SSM Agent as a means of connecting Github Actions with our EC2 instance.

    You can think of an SSM Agent as a "back door" to our instance and is something that comes by default to most EC2 instances (I believe Ubuntu and Linux instances have it built in, not sure of the others).
    Even though it is pre-installed, we need to assign an **IAM Role** to our instance to allow it to have access to SSM.

    ---

    ### _Create SSM Role_

    To create an **IAM Role** with `AmazonSSMFullAccess` permissions:
    - Open the IAM console at https://console.aws.amazon.com/iam/.
    - In the navigation pane, choose **Roles**, and then choose **Create role**.
    - Under *Select type of trusted entity*, choose **AWS service**.
    - In the Choose a use case section, choose **EC2**, and then choose **Next: Permissions**.
    - On the Attached permissions policy page, search for the `AmazonSSMFullAccess` policy, choose it, and then choose **Next: Review**.
    - On the **Review** page, type a name in the Role name box, and then type a description.
    - Choose **Create role**. The system returns you to the Roles page.

    ---

    ### _Assign SSM Role to EC2 Instance_

    Once you have the **Role** created:
    - Go to the **EC2 Instance Dashboard**
    - Go to the **Instances** link
    - Highlight the Instance
    - Click on **Actions**
    - **Instance Settings**
    - **Attach/Replace IAM Role**
    - Select the SSM Role you had created earlier
    - Hit **Apply** to save changes

    With this your EC2 Instance has access to SSM!

    ---

    ### _Github Secrets_

    With our instance being able to use the SSM Agent, we will need to provide it some details so that it can access our EC2 instance.

    This would be provided as Github Secrets. They act like environment variables for our project which is useful because we do not want anyone seeing our Secrets publicly anywhere!

    There are three Secrets we will need: `AWS_ACCESS_KEY`, `AWS_SECRET_ACCESS_KEY`, and `INSTANCE_ID`.

    Before we start, there is an article by AWS on how to find your AWS Access Key and Secret Access Key [here](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys).

    Start by going to your Github project repo:
    - Then go to your **Settings**
    - On the menu on the left, look for the link for **Secrets**
    - There, add the three *Secrets* with these keys:
    - `AWS_ACCESS_KEY_ID`
    - `AWS_SECRET_ACCESS_KEY`
    - `INSTANCE_ID`

    Once these **Secrets** are set, we are ready to move on!

    ---

    ### _Deployment script_

    This step is an extra step mainly to help make things easier although we can do everything in the next step.
    We will create a bash script which we would have the SSM run with the Github Action is triggered.

    Go to your EC2 instance and at the root of your project, create a **.sh** script:

    ```
    % vim deploy.sh
    ```
    Fill the contents with the bash commands we ran to build the project earlier:
    ```
    #!/bin/sh
    sudo git pull origin master
    sudo npm install
    cd client
    npm install
    sudo npm run-script build
    cd ..
    sudo systemctl restart nginx
    sudo pm2 restart all
    ```

    I will walk through step-by-step what we are doing at each command:
    1. `git pull origin master` makes sure we have the most up-to-date code which was triggered by a commit to the `master` branch.
    2. `npm install` installs any new dependencies for the Express side of the app.
    3. `cd client` we move into the `client` directory where our React app lives.
    4. `npm install` install any new dependencies for the React side of the app.
    5. `cd ..` return to the root of the project.
    6. `sudo systemctl restart nginx` restarts nginx so that it is serving the most recent static files.
    7. `sudo pm2 restart all` resets PM2 so that it knows to recognize any changes to the backend (This might be unnecessary because if you were following along, we set PM2 to be `--watch` which automatically recognizes any chances).

    Now that we have a deployment script we are ready for the last part where we define our **.yml** file!

    ---

    ### _yml File_

    **AWS SSM Send-Command** requires us to create a **.yml** file to execute.

    Start by going into your EC2 instance and at the root of your project create these two directories:
    ```
    % mkdir -p .github/workflows/
    ```
    This is where our **.yml** file will live.
    Create the file with:
    ```
    % sudo vim .github/workflows/deploy.yml
    ```
    Use the example to fill the contents of the **.yml** file
    ```
    name: Deploy using AWS SSM Send-Command
    on:
    push:
    branches: [master]
    jobs:
    start:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: AWS SSM Send Command
    uses: peterkimzz/[email protected]
    with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: us-east-1
    instance-ids: ${{ secrets.INSTANCE_ID }}
    comment: Deploy the master branch
    working-directory: /home/ubuntu/<YOUR PROJECT DIRECTORY>
    command: /bin/sh ./deploy.sh
    ```

    Remember the Github Secrets we set in the repo?
    This is where we use them. The Secrets we set become the values here for `aws-access-key-id`, `aws-secret-access-key`, and `instance-ids` which allows **AWS SSM Send-Command** to access our EC2 instance.

    There are 3 parts of the **.yml** file you want to make sure you change for your project:
    1. The `aws-region` should be the same Region as where you have created your EC2 instance. (If you do not know check the top left of your AWS EC2 Console to confirm the Region you are in).
    2. `working-directory` should be the working directory where you created the `deploy.sh` script.
    3. `command` should be the directions you would like to run. For our case, we created a simple script so that it does not complicate the `command` line here but you can add as many commands here as long as you are following the **.yml** syntax.

    Once the file is set, go ahead and `git add`, `commit`, and `push` to your repo and the magic should start!

    ---

    ### _Issues with Github Actions_

    I struggled with getting this to work and it took several tries.
    I found that if for whatever reason there are problems with your Github Actions deployment it helps to look through the errors.
    To find the errors go to your Github project repo:
    1. Go to **Actions**
    2. You should see a list of `workflows` or commits you have made since creating the **.yml** file.
    3. Click the most recent one.
    4. Click on the **start** link which will show you each step of the job ran.
    5. Click through to the command with a red `X` and find the errors there.

    If you do have issues feel free to reach out to me or **peterkimzz** by creating an issue [here](https://github.com/peterkimzz/aws-ssm-send-command/issues) if you feel that you have done everything 100% and it still does not work.
    **peterkimzz** was extremely responsive and helpful when I was struggling to get this working (Thank you!).

    [Back to Table of Contents](#table-of-contents)

    ---

    ## Setting up your Domain

    This is an extra step if you decided to want to buy a domain and use it for your project (I recommend it!).
    There is great satisfaction in being able to tell people to go to www.your-awesome-site.com and have people see your hard work!

    To get started you would need to first purchase a domain. I most commonly use [Google Domains](https://domains.google/) but another popular domain registrar is [GoDaddy](https://www.godaddy.com/). Whichever registrar you use, make sure you have purchased the domain that you want!

    There are two things we would need to configure to connect our project with our domain:
    - Create records on our domain DNS with our registrar.
    - Configure NGINX on our EC2 instance to recognize our domain.

    ---

    ### _Creating Domain records_

    Let's start with configuring our DNS with records:
    - Go to the **DNS** portion of your registrar.
    - Find where you can create custom resource records.

    Set the records like so:

    | Name | Type | TTL | Data |
    | ---- | :---: | :-: | ----------------------: |
    | @ | A | 1h | YOUR-ELASTIC-IP-ADDRESS |
    | www | CNAME | 1h | your-awesome-site.com |

    Once that is set we are good to move on to configure our Web Server!

    ### _Configuring our Web Server_

    Let's configure our Web Server, in our case NGINX to recognize our domain!

    Start by going to your EC2 Instance and going to our NGINX config file:
    ```
    % sudo vim /etc/nginx/sites-available/default
    ```
    Update the first section of the config file like so:
    ```
    server {
    server_name <YOUR-ELASTIC-IP> your-awesome-site.com www.your-awesome-site.com;
    ...
    ```
    We are simply adding our root domain and our sub domain (In our case with the prefix www) to our NGINX config.

    Next as we always should do after changing our NGINX config file run:
    ```
    sudo sudo systemctl restart nginx
    ```

    And Voila! You are done!
    **Note: Sometimes the domain change does not happen immediately. From my experience it can happen almost instantaneously to a few hours. If the changes haven't happened after 48 hours, double check your work to see if there are any typos or errors**.

    ---

    ## HTTPS

    **SSL** or Secure Sockets Layer allows HTTPS requests to happen. Our current project currently uses HTTP requests which can be dangerous for the potential users of your web app. Therefore I always recommend making sure that you are using HTTPS.

    For details on why HTTPS over HTTP [this article](https://ahrefs.com/blog/what-is-https/) is a pretty good deep dive on why.

    Alright! We will be working with Certbot which is provided by letsencrypt.org which is a non-profit organization which helps create SSL Certificates. They are widely used and best of all, FREE!

    ---

    ### _Installing Certbot_

    On your browser go to https://certbot.eff.org/instructions.

    There select the Software and Operating System (OS) you are using.
    For our example, we are using NGINX and Ubuntu 18.04 LTS (bionic).

    Go to your EC2 Instance and follow the instructions until they ask you to run the command:
    ```
    % sudo certbot --nginx
    ```
    After running the command certbot will prompt you with several options, the first being:
    `Which names would you like to activate HTTPS for?`
    And if your NGINX config is configured correctly, should show both your root domain as well as with the www subdomain, like so:
    ```
    1: your-awesome-site.com
    2: www.your-awesome-site.com
    ```
    I usually recommend just hitting `Enter` to activate HTTPS for both because, why not?!

    The next prompt would be:
    ```
    Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    1: No redirect - Make no further changes to the web server configuration.
    2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
    new sites, or if you're confident your site works on HTTPS. You can undo this
    change by editing your web server's configuration.
    ```
    I typically go with `2: Redirect` as it seems to make more sense to have all requests be in HTTPS.
    There are probably situations where it is not the best option but for our case we will go with this one.

    Afterwards, Certbot will go ahead and make a few changes to our NGINX config file.

    **Note: Once your site is using Https, make sure to double check your API calls and make sure that they are making calls with https:// rather than http://. This may be an unnecessary precaution but I have had issues with this in the past**.

    After a few moments checkout your domain at `your-awesome-site.com`.

    Check to make sure that there is a lock icon next to your site.

    ![secure_site](https://user-images.githubusercontent.com/41876764/86527267-2674b580-be52-11ea-9405-874f4f4ba7f0.png)

    Congratulations! You have successfully deployed a web app with HTTPS!

    [Back to Table of Contents](#table-of-contents)