|
| 1 | +# Create a deployment environment |
| 2 | + |
| 3 | +When we think about creating a deployment environment, we know this is something which won't be done regularly. You might spin up a new staging environment for testing, or when a new instance of the application is created. As a result, this might not seem like something we would want to automate. Tasks which are run frequently, like unit testing, are obvious candidates for automation. But what about those which are run sporadically? |
| 4 | + |
| 5 | +As it turns out, it can be argued that those which are run infrequently are just as important to be automated, if not more so. The reason is if a task isn't run regularly it's easier to miss steps or to lose time investigating what needs to be done. It's typically worth the initial investment up front building out an automated process which will payoff in the future by ensuring consistency and ease of use. Specific to creating a deployment environment, ensuring it's created correctly allows for the automated tasks to actually perform the deployment to run successfully. |
| 6 | + |
| 7 | +With GitHub Actions, you can use `workflow_dispatch` as a trigger for [manual execution of workflows](https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow). This is perfect for scenarios like creating a deployment environment. |
| 8 | + |
| 9 | +## Infrastructure as code |
| 10 | + |
| 11 | +[Infrastructure as code (IaC)](https://en.wikipedia.org/wiki/Infrastructure_as_code), also sometimes referred to as config as code, is a mechanism where the infrastructure required for an application is defined in a configuration file. There are numerous languages which support IaC, such as [Terraform](https://www.terraform.io/) and [Bicep](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep). By using IaC, the definition is created once and reused multiple times ensuring consistency. Rather than providing a list of instructions for a human to follow, a code file contains all of the necessary settings which is then used by an automated process (like GitHub Actions). |
| 12 | + |
| 13 | +## Scenario |
| 14 | + |
| 15 | +With the project created, the code supply chain secured, and end-to-end testing implemented, the shelter is ready to begin deploying the project. They've selected [Azure](https://azure.microsoft.com/en-us/free) as the cloud provider. Specifically, they want to use [Azure Container Apps](https://learn.microsoft.com/en-us/azure/container-apps/overview) to host the website, and [Azure Cosmos DB for MongoDB](https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/introduction) as the backend database. A [Bicep file](../../../config/main.bicep) has already been created by another contractor. You want to create a new workflow to execute on demand to create the deployment environment. |
| 16 | + |
| 17 | +> **NOTE:** For this exercise, a small amount of Azure credit will be required to store the website's image and the database. For the purposes of this workshop, the total amount should be less than $10US if you keep the website up for an entire month. At the end of the workshop, delete the resource group to ensure all billing stops. |
| 18 | +
|
| 19 | +## Exploring the Bicep file |
| 20 | + |
| 21 | +[Bicep](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep) is a domain specific language (DSL) created by Microsoft to describe and deploy Azure resources. With a Bicep file you can establish the services required, their configuration, and even set variables. This allows for flexibility and reuse, ensuring the environment is created correctly each time. |
| 22 | + |
| 23 | +> **NOTE:** A full exploration of Bicep is beyond the scope of this workshop. A [Microsoft Learn course](https://learn.microsoft.com/en-us/training/paths/fundamentals-bicep/) is available if you would like to learn more. |
| 24 | +
|
| 25 | +1. Return to your open codespace. If you closed the browser window, return to your repository and select **Code** then your codespace. |
| 26 | +1. In the **Explorer** pane, navigate to **config** and open **main.bicep**. |
| 27 | +1. When prompted, install the **Bicep extension**. This will enable color coding of the file. |
| 28 | +1. Note the following sections, each of which is used to create the necessary resources in Azure |
| 29 | + |
| 30 | + - `cosmos` |
| 31 | + - `logAnalytics` |
| 32 | + - `containerRegistry` |
| 33 | + - `containerAppEnvironment` |
| 34 | + - `containerApp` |
| 35 | + |
| 36 | + As highlighted before, by having these already defined in the file, we don't need to worry about creating these resources manually! |
| 37 | + |
| 38 | +1. At the very top of **main.bicep**, note the `namePrefix` variable. This is used to ensure the names of every resource created in Azure will be unique. |
| 39 | + |
| 40 | +## Create and configure the Azure resource group |
| 41 | + |
| 42 | +All resources created in Azure are contained in resource groups. As the name implies, this allows you to group resources together. In our situation, this allows for streamlined management and permissions, and to speed cleanup as deleting the resource group will delete all associated resources. Let's create the resource group using the [Azure command-line interface (CLI)](https://learn.microsoft.com/en-us/cli/azure/what-is-azure-cli), and create a security principal with permissions to the resource group. This account will be used in the future to create the resources and deploy the website. |
| 43 | + |
| 44 | +1. Return to your codespace. |
| 45 | +1. Open a terminal window by pressing <kbd>Ctl</kbd> - <kbd>`</kbd>. |
| 46 | +1. Log into Azure via the Azure CLI by entering the following command and pressing <kbd>Enter</kbd> (or <kbd>Return</kbd> on a Mac): |
| 47 | + |
| 48 | + ```bash |
| 49 | + az login --use-device-code |
| 50 | + ``` |
| 51 | + |
| 52 | +1. Follow the on-screen prompts to complete the authentication process. |
| 53 | +1. Create a resource group named **pets-workshop** by entering the following command and pressing <kbd>Enter</kbd> (or <kbd>Return</kbd> on a Mac): |
| 54 | + |
| 55 | + ```bash |
| 56 | + az group create -n pets-workshop -l westus |
| 57 | + ``` |
| 58 | + |
| 59 | +1. Obtain your Azure subscription ID (used in the next step) by entering the following command and pressing <kbd>Enter</kbd> (or <kbd>Return</kbd> on a Mac): |
| 60 | + |
| 61 | + ```bash |
| 62 | + az account show --query id -o tsv |
| 63 | + ``` |
| 64 | + |
| 65 | +1. Create the service principal to be used to manage the resource group by entering the following command, replacing **<SUBSCRIPTION_ID>** with your subscription ID obtained in the prior step, and pressing <kbd>Enter</kbd> (or <kbd>Return</kbd> on a Mac): |
| 66 | + |
| 67 | + ```bash |
| 68 | + az ad sp create-for-rbac --name pets-workshop-app --role contributor --scopes /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/pets-workshop --sdk-auth |
| 69 | + ``` |
| 70 | + |
| 71 | + This command will return a [JSON](https://en.wikipedia.org/wiki/JSON) object which serves as the credentials used to create the resources on Azure and deploy the project. |
| 72 | + |
| 73 | +1. Copy the JSON to a scratchpad such as Notepad or Notes. You will use this object in the next step. |
| 74 | + |
| 75 | +> **IMPORTANT:** In the real world, this should be treated the same as any credential or username and password. It should be properly secured and not shared with anyone. |
| 76 | + |
| 77 | +## Securing secrets in a repository |
| 78 | + |
| 79 | +In the prior exercise you created a resource group and a credential with access to it. This credential should be treated as a sensitive value as an attacker could use it to perform undesired operations. The question then becomes, if this credential is to be used by our workflow, how do we ensure it's properly secured? |
| 80 | +
|
| 81 | +You can store sensitive values using [encrypted secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets). Encrypted secrets are write-only values which can be accessed by a workflow but cannot be read by other processes. This allows you to securely store connection strings, tokens and other credentials without fear of them being leaked to other processes. |
| 82 | +
|
| 83 | +Let's create the secrets required for our workflow. |
| 84 | + |
| 85 | +1. In a new browser tab, navigate to your repository. |
| 86 | +1. Select the **Settings** tab. |
| 87 | +1. On the left side, expand **Secrets and variables** and select **Actions**. |
| 88 | +1. Create a new repository secret to store the credentials by selecting **New repository secret**, entering the following values (replacing `<THE JSON FROM THE PRIOR STEP>` with the JSON you created previously), and selecting **Add secret**: |
| 89 | + |
| 90 | + - **Name**: `AZURE_CREDENTIALS` |
| 91 | + - **Secret**: `<THE JSON FROM THE PRIOR STEP>` |
| 92 | + |
| 93 | +1. Create a second repository secret to store the subscription ID by selecting **New repository secret**, entering the following values (replacing `<SUBSCRIPTION_ID>` with your subscription ID), and selecting **Add secret**: |
| 94 | + |
| 95 | + - **Name**: `AZURE_SUBSCRIPTION` |
| 96 | + - **Secret**: `<SUBSCRIPTION_ID>` |
| 97 | + |
| 98 | +Not all values need to be secured. For sensitive information, like credentials or your subscription ID, it's best to store those properly. But other values, like the name of your resource group and the prefix you'll use for the other resources to be created, don't need to be hidden. These are perfect for variables. Variables behave in much the same way as secrets, except they're not encrypted or hidden from repository owners. |
| 99 | +
|
| 100 | +Let's create variables for the name of the resource group and your prefix: |
| 101 | + |
| 102 | +1. On the **Actions secrets and variables** screen, select the **Variables** tab. |
| 103 | +1. Create a variable for the name of the resource group by selecting **New variable**, entering the following values, and selecting **Add variable**: |
| 104 | + |
| 105 | + - **Name**: `AZURE_RG` |
| 106 | + - **Value**: `pets-workshop` |
| 107 | + |
| 108 | +1. Create a variable for the prefix to use for naming other resources by selecting **New variable**, entering the following values, replacing `<PREFIX_NAME>` with five random letters, and selecting **Add variable**: |
| 109 | + |
| 110 | + - **Name**: `AZURE_PREFIX` |
| 111 | + - **Value**: `<PREFIX_NAME>` |
| 112 | + |
| 113 | +## Creating the workflow |
| 114 | + |
| 115 | +You've now configured Azure and added secrets & variables to your repository. You're now ready to create the workflow! This workflow will run on-demand, and create the resources on Azure. This will both make it easier to create everything and ensure it's done correctly. |
| 116 | +
|
| 117 | +Previously you created a workflow by using the **Actions** tab in your repository. While this works, it's not always ideal as you will often want better support from your editor. Since workflows are defined as [YML](https://en.wikipedia.org/wiki/YAML) files |
| 118 | + |
| 119 | +1. Return to your codespace. |
| 120 | +1. If the **Terminal** window isn't already open, open it by pressing <kbd>Ctl</kbd> - <kbd>`</kbd> on your keyboard. |
| 121 | +1. Switch to the `main` branch, pull any changes currently on the server to your codespace, and create a new branch by entering the following command in the terminal window and pressing <kbd>Enter</kbd> (or <kbd>Return</kbd> on a Mac): |
| 122 | +
|
| 123 | + ```bash |
| 124 | + git checkout main |
| 125 | + git pull |
| 126 | + git checkout -b add-resource-workflow |
| 127 | + ``` |
| 128 | +
|
| 129 | +1. In the **Explorer** pane, open the **.github** > **workflows** folder. |
| 130 | +1. Create a new file in the **workflows** folder named **create-resources.yml**. |
| 131 | +1. Define the workflow by pasting the following code into **create-resources.yml**: |
| 132 | +
|
| 133 | + ```yml |
| 134 | + name: Create Azure resources |
| 135 | + on: [workflow_dispatch] |
| 136 | + jobs: |
| 137 | + build-and-deploy: |
| 138 | + runs-on: ubuntu-latest |
| 139 | + steps: |
| 140 | +
|
| 141 | + # Checkout code |
| 142 | + - uses: actions/checkout@main |
| 143 | +
|
| 144 | + # Log into Azure |
| 145 | + - uses: azure/login@v1 |
| 146 | + with: |
| 147 | + creds: ${{ secrets.AZURE_CREDENTIALS }} |
| 148 | +
|
| 149 | + # Deploy Bicep file |
| 150 | + - name: create resources |
| 151 | + uses: azure/arm-deploy@v1 |
| 152 | + with: |
| 153 | + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION }} |
| 154 | + resourceGroupName: ${{ secrets.AZURE_RG }} |
| 155 | + template: ${{ github.workspace }}/config/main.bicep |
| 156 | + parameters: 'namePrefix=${{ secrets.AZURE_PREFIX }}' |
| 157 | + failOnStdErr: false |
| 158 | + ``` |
| 159 | +
|
| 160 | + The workflow is set to run on `workflow_dispatch`, which is a manual trigger. The steps checkout the code, log into Azure using the credentials you created and stored previously, then create the resources defined in the **main.bicep** in the resource group you created with the prefix you defined. |
| 161 | +
|
| 162 | +1. Stage, commit and push all changes to the repository by entering the following command in the terminal window and pressing <kbd>Enter</kbd> (or <kbd>Return</kbd> on a Mac): |
| 163 | +
|
| 164 | + ```bash |
| 165 | + git add . |
| 166 | + git commit -m "Defined workflow" |
| 167 | + git push -u origin add-resource-workflow |
| 168 | + ``` |
| 169 | +
|
| 170 | +1. Obtain the number for the issue you created for creating deployment environment by entering the following command in the terminal window and pressing <kbd>Enter</kbd> (or <kbd>Return</kbd> on a Mac): |
| 171 | +
|
| 172 | + ```bash |
| 173 | + gh issue list |
| 174 | + ``` |
| 175 | +
|
| 176 | +1. Create a pull request (PR) for the newly created branch referencing the issue, replacing <ISSUE_NUMBER> with the issue you obtained in the prior step by entering the following command in the terminal window and pressing <kbd>Enter</kbd> (or <kbd>Return</kbd> on a Mac): |
| 177 | +
|
| 178 | + ```bash |
| 179 | + gh pr create -t "Add resource creation workflow" -b "Resolves #<ISSUE_NUMBER>" |
| 180 | + ``` |
| 181 | +
|
| 182 | +1. Merge the PR you just created by entering the following command, replacing <PR_NUMBER> with the newly generated PR number, in the terminal window and pressing <kbd>Enter</kbd> (or <kbd>Return</kbd> on a Mac): |
| 183 | +
|
| 184 | + ```bash |
| 185 | + gh pr merge <PR_NUMBER> |
| 186 | + ``` |
| 187 | +
|
| 188 | + > **IMPORTANT:** Normally you would go through a standard review flow before merging a PR. Because we're working through a set of exercises as part of a workshop we're going to shortcut a couple of steps. |
| 189 | +
|
| 190 | +## Running the workflow |
| 191 | +
|
| 192 | +You've prepped everything on both Azure and your repository, and created the workflow. Only one last thing to do - run the workflow! This will create the environment on Azure for deploying the application (which you'll do in the next exercise). |
| 193 | + |
| 194 | +1. Navigate to your repository. |
| 195 | +1. Select the **Actions** tab. |
| 196 | +1. On the list of workflows, select **Create Azure resources**. |
| 197 | +1. Select the ellipsis (**...**) next to **Create Azure resources** and select **Run workflow**. |
| 198 | + |
| 199 | + The workflow will now run and create the resources! This will take several minutes. You can navigate into the workflow run to view the log and track the progress. |
| 200 | + |
| 201 | +1. When the workflow completes, return to your codespace. |
| 202 | +1. Obtain the URL for the newly created Azure Container App by entering the following command in the terminal window and pressing <kbd>Enter</kbd> (or <kbd>Return</kbd> on a Mac): |
| 203 | + |
| 204 | + ```bash |
| 205 | + az containerapp list --query "[].properties.configuration.ingress.fqdn" -o tsv |
| 206 | + ``` |
| 207 | + |
| 208 | +1. Navigate to the site by using <kbd>Ctl</kbd> - **Click** (or <kbd>Cmd</kbd> - **Click** on a Mac) on the URL displayed. |
| 209 | +1. You will be presented with a "Hello, world" page. (Don't worry - you'll deploy your site shortly!) |
| 210 | + |
| 211 | +## Summary and next steps |
| 212 | + |
| 213 | +Congratulations! You have new defined a workflow which uses infrastructure as code (IaC) to create the resources necessary for deployment. This allows you to quickly create a consistent environment, reducing overhead and errors. Let's close everything out by [implementing continuous deployment](8-continuous-deployment.md). |
| 214 | +
|
| 215 | +## Resources |
0 commit comments