├── .gitignore
├── Agenda.md
├── README.md
├── challenges.md
├── challenges
├── 00-gettingstarted
│ ├── README.md
│ └── local.md
├── 01-connectingtoazure
│ └── README.md
├── 02-aci-helloworld
│ └── README.md
├── 03-azurevm
│ └── README.md
├── 04-terraformcount
│ └── README.md
├── 05-terraformmodules
│ └── README.md
├── 06-publicmoduleregistry
│ └── README.md
├── 07-remotebackend
│ └── README.md
├── 08-setupterraformenterprise
│ └── README.md
├── 09-privatemoduleregistry
│ └── README.md
└── 10-sentinelpolicy
│ └── README.md
├── docs
└── AzureTerraformWorkshopPresentation.pptx
├── event-invitation.md
├── examples
├── simple-rg
│ └── main.tf
└── ssh
│ └── main.tf
├── img
├── 2018-04-07-15-08-41.png
├── 2018-04-07-16-54-28.png
├── 2018-04-14-12-58-33.png
├── 2018-04-14-12-59-54.png
├── 2018-04-14-13-10-52.png
├── 2018-04-14-13-21-32.png
├── 2018-04-14-14-04-56.png
├── 2018-04-14-14-10-32.png
├── 2018-04-14-14-11-20.png
├── 2018-04-14-14-12-05.png
├── 2018-04-14-14-13-01.png
├── 2018-04-14-14-13-52.png
├── 2018-04-14-14-15-02.png
├── 2018-04-15-13-09-55.png
├── 2018-04-15-19-23-40.png
├── 2018-04-16-20-02-58.png
├── 2018-04-16-20-03-30.png
├── 2018-05-07-18-08-30.png
├── 2018-05-07-18-11-33.png
├── 2018-05-07-18-13-28.png
├── 2018-05-07-18-20-56.png
├── 2018-05-07-18-29-10.png
├── 2018-05-09-09-10-24.png
├── 2018-05-09-10-20-28.png
├── 2018-05-09-14-55-42.png
├── 2018-05-10-17-14-51.png
├── 2018-05-10-17-17-27.png
├── 2018-05-10-17-37-05.png
├── 2018-05-10-17-40-35.png
├── 2018-05-11-11-22-22.png
├── 2018-05-11-11-26-22.png
├── 2018-05-14-07-27-11.png
├── 2018-05-14-08-18-48.png
├── 2018-05-28-12-25-01.png
├── 2018-05-28-12-27-31.png
├── 2018-05-28-12-29-06.png
├── 2018-05-28-12-30-33.png
├── 2018-05-28-13-58-49.png
├── 2018-05-28-14-01-30.png
├── 2018-05-28-14-03-05.png
├── 2018-05-28-14-04-39.png
├── 2018-05-28-14-05-39.png
├── 2018-05-28-14-09-39.png
├── 2018-06-07-16-23-29.png
├── 2018-11-02-12-59-21.png
├── 2019-05-08-09-24-12.png
└── 2019-05-08-09-27-19.png
└── solutions
├── 01-connectingtoazure
└── main.tf
├── 02-aci-helloworld
└── main.tf
├── 03-azurevm
└── main.tf
├── 04-terraformcount
└── main.tf
├── 05-terraformmodules
├── environments
│ └── dev
│ │ └── main.tf
└── modules
│ └── my_virtual_machine
│ └── main.tf
└── 06-publicmoduleregistry
└── main.tf
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled files
2 | *.tfstate
3 | *.tfstate.backup
4 |
5 | # Module directory
6 | .terraform/
7 | Notes.md
8 |
9 | *.tfvars
10 | *terraform.tfstate*
11 | *.terraform.tfstate.lock.info
12 | ~*.pptx
13 | __*.*
14 | .DS_Store
15 | demo/
16 |
17 | id_rsa*
18 |
--------------------------------------------------------------------------------
/Agenda.md:
--------------------------------------------------------------------------------
1 | # Agenda
2 |
3 | | Time | Topic |
4 | | ---- | ----- |
5 | | 9:00-9:30 | Getting Started & Overview |
6 | | 9:30-10:00 | Terraform OSS & Workflow |
7 | | 10:00-10:30 | Challenge 00 - Getting Started |
8 | | | Challenge 01 - Connecting to Azure |
9 | | 10:30-11:00 | State, Variables & Outputs |
10 | | 10:30-11:00 | Challenge 02 - Azure Container Instance |
11 | | 11:00-11:30 | Interpolations & Modules |
12 | | 11:30-12:00 | Challenge 03 - Azure Virtual Machine |
13 | | 12:00-1:00 | Lunch / Solutions Round Table |
14 | | 1:00-1:30 | Backend & Internals |
15 | | 1:30-2:00 | Challenge 06 - Public Module Registry |
16 | | 2:00-3:00 | Terraform Enterprise, Workspaces, & State Management |
17 | | 3:00-3:30 | Collaboration & Private Module Registry |
18 | | 3:30-4:00 | Governance using Sentinel |
19 | | 4:00-4:30 | Closeout and Questions |
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Terraform Workshop
2 |
3 | This repository contains materials for the Infrastructure as Code - Building Azure infrastructure using Terraform and Terraform Enterprise event series. This includes attendees challenge guides, slides, and source code.
4 |
5 | Workshop Locations:
6 |
7 | - Wednesday April 24th, 2019 [Nashville, TN](http://www.cvent.com/d/l6qs97)
8 | - Wednesday May 14th, 2019 [Columbus, OH](http://www.cvent.com/d/86qcx5)
9 | - Wednesday May 21st, 2019 [New York, NY](http://www.cvent.com/d/06qw2x)
10 |
11 | ## Surveys
12 |
13 | Handed out at the end of the day.
14 |
15 | ## Presentation
16 |
17 | The Power Point presentation can be found here: [AzureTerraformWorkshopPresentation](docs/AzureTerraformWorkshopPresentation.pptx)
18 |
19 | ## Challenges
20 |
21 | The Challenge description can be found here: [Challenges](challenges.md)
22 |
23 | ## Resources
24 |
25 | - https://www.terraform.io/docs/providers/azurerm
26 | - http://aka.ms/tfhub Microsoft documentation
27 | - https://github.com/kamatama41/tfenv Terraform Version Switcher
28 |
29 | ## Previous Locations
30 |
31 | - Monday November 5th, 2018 [Tampa, FL](https://www.cardinalsolutions.com/events/2018/11/11-5-18-tpainfracstructureascode)
32 | - Monday June 11th, 2018 [New York City, NY](https://www.eventbrite.com/e/provision-and-manage-microsoft-azure-infrastructure-tickets-46152026955)
33 | - Tuesday June 5th, 2018 [Columbus, OH](https://www.eventbrite.com/e/provision-and-manage-microsoft-azure-infrastructure-tickets-45781193783)
34 | - Thursday May 31st, 2018 [Atlanta, GA](https://www.eventbrite.com/e/provision-and-manage-microsoft-azure-infrastructure-tickets-44854601320)
35 | - Wednesday May 23rd, 2018 [Chicago, IL](https://www.eventbrite.com/e/provision-and-manage-microsoft-azure-infrastructure-tickets-45562831656)
--------------------------------------------------------------------------------
/challenges.md:
--------------------------------------------------------------------------------
1 | # Building Azure infrastructure using Terraform and Terraform Enterprise Challenge Guide
2 |
3 | Below is a series of "challenges" or guided exercises to help attendees learn about Terraform and how to use it to create Azure Resources. These are not meant the be "hands-on labs" or step-by-step guides. The goal is to provide a series of exercises that have an expected outcome. Some steps and code will be provided. In the end, the hands-on experience should lead to a deeper level of learning.
4 |
5 | ## Prerequisites
6 |
7 | - Laptop
8 | - Azure account with access to deploy at least 10 cores and permissions to generate a service principal. A trail Azure account can be created for this event.
9 | - Github Account (Free)
10 | - Terraform Enterprise Account. Attendees will be provided a 30-day trial.
11 |
12 | Setting up your laptop for the challenges is done in Challenge 00 - Getting Started.
13 |
14 | ## Challenges
15 |
16 | For each challenge, change your working directory to the folder for that challenge.
17 | Each challenge is meant to be independent of each other.
18 | If you get stuck, refer to the `solutions` directory for a working solution to the challenge.
19 |
20 | ### Challenge 00: [Getting Started](challenges/00-gettingstarted/README.md)
21 |
22 | ### Challenge 01: [Connecting to Azure](challenges/01-connectingtoazure/README.md)
23 |
24 | ### Challenge 02: [Azure Container Instance](challenges/02-aci-helloworld/README.md)
25 |
26 | ### Challenge 03: [Azure Virtual Machine](challenges/03-azurevm/README.md)
27 |
28 | ### Challenge 04: [Terraform Count](challenges/04-terraformcount/README.md)
29 |
30 | ### Challenge 05: [Terraform Modules](challenges/05-terraformmodules/README.md)
31 |
32 | ### Challenge 06: [Public Module Registry](challenges/06-publicmoduleregistry/README.md)
33 |
34 | ### Challenge 07: [Remote Backend](challenges/07-remotebackend/README.md)
35 |
36 | ### Challenge 08: [Setup Terraform Enterprise](challenges/08-setupterraformenterprise/README.md)
37 |
38 | ### Challenge 09: [Private Module Registry](challenges/09-privatemoduleregistry/README.md)
39 |
40 | ### Challenge 10: [Sentinel Policy](challenges/10-value/README.md)
41 |
--------------------------------------------------------------------------------
/challenges/00-gettingstarted/README.md:
--------------------------------------------------------------------------------
1 | # 00 - Getting Started
2 |
3 | ## Expected Outcome
4 |
5 | In this challenge, you will connect to the [Azure Cloud Shell](https://azure.microsoft.com/en-us/features/cloud-shell/) that will be needed for future challenges.
6 |
7 | In this challenge, you will:
8 |
9 | - Login to the Azure Portal
10 | - Verify `az` installation
11 | - Verify `terraform` installation
12 | - Create a folder structure to complete challenges
13 |
14 | > Note: If you would rather complete the challenges from you local worskstation, detailed instructions can be found [here](local.md).
15 | ## How to
16 |
17 | ### Login to the Azure Portal
18 |
19 | Navigate to [https://portal.azure.com](https://portal.azure.com) and login with your Azure Credentials.
20 |
21 | This workshop will require that you have access to an Azure Subscription with at least Contributor rights to create resources. If you do not currently have access you can create a trial account by going to [https://azure.microsoft.com/en-us/free](https://azure.microsoft.com/en-us/free) and registering for a 3-month trail.
22 |
23 | Signing up for a trial requires:
24 |
25 | - A unique Microsoft Live Account that has not registered for a trial for in the past
26 | - A Credit Card, used to verify identity and will not be charged unless you opt-in after the trial is over
27 |
28 | > If you are having issues with this access, please alert the instructor ASAP as this will prevent you from completing the challenges.
29 |
30 | ### Open the Cloud Shell
31 |
32 | Located at the top of the page is the button open the Azure Cloud Shell inside the Azure Portal.
33 |
34 | 
35 |
36 | > Note: Another option is to use the full screen Azure Cloud Shell at [https://shell.azure.com/](https://shell.azure.com/).
37 |
38 | The first time you connect to the Azure Cloud Shell you will be prompted to setup an Azure File Share that you will persist the environment.
39 |
40 | 
41 |
42 | Click the "Bash (Linux)" option.
43 |
44 | Select the Azure Subscription and click "Create storage":
45 |
46 | 
47 |
48 | After a few seconds you should see that your storage account has been created:
49 |
50 | 
51 |
52 | > Note: Behind the scenes this is creating a new Resource Group with the name `cloud-shell-storage-eastus` (or which ever region you defaulted to). If you need more information, it can be found [here](https://docs.microsoft.com/en-us/azure/cloud-shell/persisting-shell-storage).
53 |
54 | SUCCESS!
55 | You are now logged into the Azure Cloud Shell which uses your portal session to automatically authenticate you with the Azure CLI and Terraform.
56 |
57 | ### Verify Utilities
58 |
59 | In the Cloud Shell type the following commands and verify that the utilities are installed:
60 |
61 | `az -v`
62 |
63 | View Output
64 |
65 |
66 | ```sh
67 | $ az -v
68 | azure-cli (2.0.64)
69 |
70 | acr 2.2.6
71 | acs 2.4.1
72 | advisor 2.0.0
73 | ams 0.4.5
74 | appservice 0.2.19
75 | backup 1.2.4
76 | batch 4.0.1
77 | batchai 0.4.8
78 | billing 0.2.1
79 | botservice 0.2.0
80 | cdn 0.2.3
81 | cloud 2.1.1
82 | cognitiveservices 0.2.5
83 | command-modules-nspkg 2.0.2
84 | configure 2.0.23
85 | consumption 0.4.2
86 | container 0.3.16
87 | core 2.0.64
88 | cosmosdb 0.2.10
89 | deploymentmanager 0.1.0
90 | dla 0.2.5
91 | dls 0.1.9
92 | dms 0.1.3
93 | eventgrid 0.2.3
94 | eventhubs 0.3.5
95 | extension 0.2.5
96 | feedback 2.2.1
97 | find 0.3.2
98 | hdinsight 0.3.3
99 | interactive 0.4.3
100 | iot 0.3.8
101 | iotcentral 0.1.6
102 | keyvault 2.2.15
103 | kusto 0.2.2
104 | lab 0.1.7
105 | maps 0.3.4
106 | monitor 0.2.13
107 | network 2.4.0
108 | nspkg 3.0.3
109 | policyinsights 0.1.3
110 | privatedns 1.0.0
111 | profile 2.1.5
112 | rdbms 0.3.10
113 | redis 0.4.2
114 | relay 0.1.4
115 | reservations 0.4.2
116 | resource 2.1.14
117 | role 2.6.1
118 | search 0.1.1
119 | security 0.1.1
120 | servicebus 0.3.5
121 | servicefabric 0.1.18
122 | signalr 1.0.0
123 | sql 2.2.3
124 | sqlvm 0.1.1
125 | storage 2.4.1
126 | telemetry 1.0.2
127 | vm 2.2.20
128 |
129 | Python location '/opt/az/bin/python3'
130 | Extensions directory '~/.azure/cliextensions'
131 |
132 | Python (Linux) 3.6.5 (default, May 2 2019, 00:44:44)
133 | [GCC 5.4.0 20160609]
134 |
135 | Legal docs and information: aka.ms/AzureCliLegal
136 |
137 | Your CLI is up-to-date.
138 | ```
139 |
140 |
141 |
142 | `terraform -v`
143 |
144 | View Output
145 |
146 |
147 | ```sh
148 | $ terraform -v
149 | Terraform v0.11.13
150 | ```
151 |
152 |
153 |
154 |
155 | ### Verify Subscription
156 |
157 | Run the command `az account list -o table`.
158 |
159 | ```sh
160 | az account list -o table
161 | Name CloudName SubscriptionId State IsDefault
162 | ------------------------------- ----------- ------------------------------------ ------- -----------
163 | Visual Studio Premium with MSDN AzureCloud xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx Enabled True
164 | Another sub1 AzureCloud xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx Enabled False
165 | Another sub2 AzureCloud xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx Enabled False
166 | Another sub3 AzureCloud xxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx Enabled False
167 | ```
168 |
169 | If you have more than subscription, make sure that subscription is set as default using the subscription name:
170 |
171 | ```sh
172 | az account set -s 'Visual Studio Premium with MSDN'
173 | ```
174 |
175 | ### Create Challenge Scaffolding
176 |
177 | To make things easy for the challenges, let's create a folder structure to hold the terraform configuration we will create.
178 |
179 | Make sure you are in the home directory:
180 |
181 | ```sh
182 | cd ~/
183 | ```
184 |
185 | Run the following in the azure cloud shell, this will simply create a folder structure for you to place your Terraform configuration:
186 |
187 | ```sh
188 | mkdir AzureWorkChallenges && cd AzureWorkChallenges && mkdir challenge01 && mkdir challenge02 && mkdir challenge03 && mkdir challenge04 && mkdir challenge05 && mkdir challenge06 && mkdir challenge07
189 | ```
190 |
191 | What you should end up with is a structure like this:
192 |
193 | ```sh
194 | AzureWorkChallenges
195 | |- challenge01
196 | |- challenge02
197 | |- challenge03
198 | |- challenge04
199 | |- challenge05
200 | |- challenge06
201 | |- challenge07
202 | ```
203 |
--------------------------------------------------------------------------------
/challenges/00-gettingstarted/local.md:
--------------------------------------------------------------------------------
1 | # Installing Challenge Requirements Locally
2 |
3 | This section is only needed if you wish to install all the tooling on your local machine.
4 |
5 | > NOTE: If you are using windows, it is advised that you use the git-bash terminal to execute the workshop commands.
6 |
7 | ## How to
8 |
9 | ### Download the Azure CLI 2.0
10 |
11 | To make some of the steps easier we will use the Azure CLI and authenticate to Azure.
12 |
13 | Download and install the latest Azure CLI:
14 |
15 | For **Windows** - [Download Here](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-apt?view=azure-cli-latest)
16 |
17 | For **Mac** - [Download Here](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-macos?view=azure-cli-latest)
18 |
19 | > If you are having issues, more information can be found [here](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest)
20 |
21 | Verify the installation by running `az -v`, you should see something like this, version 2.0.0 or higher is needed:
22 |
23 | ```sh
24 | $ az -v
25 | azure-cli (2.0.32)
26 |
27 | ... Other python dependencies
28 |
29 | Python (Darwin) 3.6.5 (default, Apr 25 2018, 14:26:36)
30 | [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]
31 |
32 | Legal docs and information: aka.ms/AzureCliLegal
33 | ```
34 |
35 | ### Login to Azure
36 |
37 | This workshop will require that you have access to an Azure Subscription with at least Contributor rights to create resources and the ability to generate a service principal for the subscription. If you do not currently have access you can create a trial account by going to [https://azure.microsoft.com/en-us/free](https://azure.microsoft.com/en-us/free) and registering for a 3-month trail.
38 |
39 | Signing up for a trial requires:
40 |
41 | - A unique Microsoft Live Account that has not registered for a trial for in the past
42 | - A Credit Card, used to verify identity and will not be charged unless you opt-in after the trial is over
43 |
44 | > If you are having issues with this access, please alert the instructor ASAP as this will prevent you from completing the challenges.
45 |
46 | Login with the Azure CLI by running `az login`.
47 |
48 | ```sh
49 | $ az login
50 | To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code XXXXXXXX to authenticate.
51 | ```
52 |
53 | Navigate to [https://microsoft.com/devicelogin](https://microsoft.com/devicelogin) and enter the code given in the CLI, then log in using the account that has access to Azure.
54 |
55 | Once complete, verify Azure CLI Access by running `az account show -o table`.
56 |
57 | ```sh
58 | $ az account show -o table
59 | EnvironmentName IsDefault Name State TenantId
60 | ----------------- ----------- ------------------------------- ------- ------------------------------------
61 | AzureCloud True Visual Studio Premium with MSDN Enabled GUID
62 | ```
63 |
64 | You are now connecting to Azure from the Azure CLI!
65 |
66 | As one last step here, login to the [Azure Portal](https://portal.azure.com/), this will be useful to see the resources get created in future challenges.
67 |
68 | ### Download Terraform
69 |
70 | In this workshop we will be using terraform 0.11.7 for all of the challenges.
71 |
72 | Navigate to the downloads page [https://releases.hashicorp.com/terraform/0.11.7/](https://releases.hashicorp.com/terraform/0.11.7/) and select the `.zip` file for your operating system.
73 |
74 | For **Windows** - [Download Here](https://releases.hashicorp.com/terraform/0.11.7/terraform_0.11.7_windows_amd64.zip)
75 |
76 | For **Mac** - [Download Here](https://releases.hashicorp.com/terraform/0.11.7/terraform_0.11.7_darwin_amd64.zip)
77 |
78 | Once downloaded, extract the contents which is just a `terraform` binary and copy it to a folder inside your PATH environment variable.
79 |
80 | For **Windows** - create a new directory and add it to your PATH environment variable
81 |
82 | For **Mac** - typically `/usr/local/bin`
83 |
84 | > If you are having issues, more information can be found [here](https://www.terraform.io/intro/getting-started/install.html)
85 |
86 | Verify the installation by running `terraform -v`, you should see something like this:
87 |
88 | ```sh
89 | $ terraform -v
90 | Terraform v0.11.7
91 | ```
92 |
93 | The latest release can always be found on the [Terraform Website](https://www.terraform.io/downloads.html)
94 |
95 | ### Download Visual Studio Code
96 |
97 | *or any other text editor...*
98 |
99 | You can Download the latest version here:
100 |
101 | https://code.visualstudio.com/Download
102 |
103 | 
104 |
105 | Optionally you can also install the Terraform Extension [here](https://marketplace.visualstudio.com/items?itemName=mauve.terraform)
106 |
107 | ### Clone this repository
108 |
109 | Install git by going to [here](https://git-scm.com/downloads) and downloading the latest git version.
110 |
111 | Once installed, open up a terminal and change directory into a path that you would like to work out of.
112 | Then open the repository in VS Code.
113 |
114 | ```sh
115 | cd ~/Projects/
116 | git clone https://github.com/CardinalNow/TerraformWorkshop.git
117 | code TerraformWorkshop
118 | ```
119 |
120 | > If running `code TerraformWorkshop` doesn't launch VS Code, open up VS Code manually and open the folder you cloned the repository to.
121 |
122 | ### Github Access
123 |
124 | If you already have a github account you can skip this step.
125 |
126 | Github repositories will be needed to complete some of the later challenges.
127 |
128 | Sign up for a free github.com account by going to [https://github.com/join](https://github.com/join) and following the instructions.
129 |
130 | Once created, login.
131 |
--------------------------------------------------------------------------------
/challenges/01-connectingtoazure/README.md:
--------------------------------------------------------------------------------
1 | # 01 - Connection To Azure
2 |
3 | ## Expected Outcome
4 |
5 | In this challenge, you will use Terraform from the Azure Cloud Shell to create simple infrastructure in your Azure Subscription.
6 |
7 | In this challenge, you will:
8 |
9 | - Initialize Terraform
10 | - Run a `plan` on simple a simple resource
11 | - Run an `apply` to create Azure infrastructure
12 | - Run a `destroy` to remove Azure infrastructure
13 |
14 | ## How To
15 |
16 | ### Create Terraform Configuration
17 |
18 | From the Cloud Shell, change directory into a folder specific to this challenge. If you created the scaffolding in Challenge 00, then then you can use the command `cd ~/AzureWorkChallenges/challenge01/`.
19 |
20 | Create a file named `main.tf` and add a single Resource Group resource.
21 |
22 | ```hcl
23 | resource "azurerm_resource_group" "test" {
24 | name = "challenge01-rg"
25 | location = "eastus"
26 | }
27 | ```
28 |
29 | This will create a simple Resource Group and allow you to walk through the Terraform Workflow.
30 |
31 | ### Run the Terraform Workflow
32 |
33 | `terraform init`
34 | View Output
35 |
36 |
37 | ```sh
38 | $ terraform init
39 |
40 | Initializing provider plugins...
41 |
42 | Terraform has been successfully initialized!
43 |
44 | You may now begin working with Terraform. Try running "terraform plan" to see
45 | any changes that are required for your infrastructure. All Terraform commands
46 | should now work.
47 |
48 | If you ever set or change modules or backend configuration for Terraform,
49 | rerun this command to reinitialize your working directory. If you forget, other
50 | commands will detect it and remind you to do so if necessary.
51 | ```
52 |
53 |
54 |
55 |
56 | ---
57 | `terraform plan`
58 |
59 | View Output
60 |
61 |
62 | ```sh
63 | $ terraform plan
64 | Refreshing Terraform state in-memory prior to plan...
65 | The refreshed state will be used to calculate this plan, but will not be
66 | persisted to local or remote state storage.
67 |
68 |
69 | ------------------------------------------------------------------------
70 |
71 | An execution plan has been generated and is shown below.
72 | Resource actions are indicated with the following symbols:
73 | + create
74 |
75 | Terraform will perform the following actions:
76 |
77 | + azurerm_resource_group.main
78 | id:
79 | location: "eastus"
80 | name: "challenge01-rg"
81 | tags.%:
82 |
83 |
84 | Plan: 1 to add, 0 to change, 0 to destroy.
85 |
86 | ------------------------------------------------------------------------
87 |
88 | Note: You didn't specify an "-out" parameter to save this plan, so Terraform
89 | can't guarantee that exactly these actions will be performed if
90 | "terraform apply" is subsequently run.
91 | ```
92 |
93 |
94 |
95 |
96 | ---
97 | `terraform apply`
98 | View Output
99 |
100 |
101 | ```sh
102 | $ terraform apply
103 |
104 | An execution plan has been generated and is shown below.
105 | Resource actions are indicated with the following symbols:
106 | + create
107 |
108 | Terraform will perform the following actions:
109 |
110 | + azurerm_resource_group.main
111 | id:
112 | location: "eastus"
113 | name: "challenge01-rg"
114 | tags.%:
115 |
116 |
117 | Plan: 1 to add, 0 to change, 0 to destroy.
118 |
119 | Do you want to perform these actions?
120 | Terraform will perform the actions described above.
121 | Only 'yes' will be accepted to approve.
122 |
123 | Enter a value: yes
124 |
125 | azurerm_resource_group.main: Creating...
126 | location: "" => "eastus"
127 | name: "" => "challenge01-rg"
128 | tags.%: "" => ""
129 | azurerm_resource_group.main: Creation complete after 1s (ID: /subscriptions/.../resourceGroups/challenge01-rg)
130 |
131 | Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
132 | ```
133 |
134 |
135 |
136 | ---
137 |
138 | Congrats, you just created your first Azure resource using Terraform!
139 |
140 | ### Verify in the Azure Portal
141 |
142 | Head over to the [Azure Portal](https://portal.azure.com/)
143 |
144 | View all Resource Groups and you should see the recently created Resource Group.
145 | 
146 |
147 | ### Scale Resources
148 |
149 | Now add a new Resource Group resource that scales with a `count` parameter.
150 |
151 | > Note: This is ADDING another `resource` block in addition to the one you have already created.
152 |
153 | ```hcl
154 | resource "azurerm_resource_group" "count" {
155 | name = "challenge01-rg-${count.index}"
156 | location = "eastus"
157 | count = 2
158 | }
159 | ```
160 |
161 | Run another `terraform plan` then `terraform apply` and validate the resource groups have been created.
162 |
163 | ---
164 |
165 | ## How To - Part 2 (Import Resources)
166 |
167 | ### Create Infrastructure in the Portal
168 |
169 | Navigate to the Azure Portal and click on the "Resource groups" item on the left side and then click "+ Add":
170 |
171 | 
172 |
173 | In the Resource Group create blade give the resource group the name "myportal-rg" and click "Review + Create" -> "Create":
174 |
175 | 
176 |
177 |
178 | Once the Resource Group is created, navigate to it.
179 |
180 | Find the "+ Add" button and click it:
181 |
182 | 
183 |
184 | Search for "Storage Account" and click the first item and then click "Create" :
185 |
186 | 
187 |
188 |
189 |
190 | In the Storage Account create blade, fill out the following:
191 |
192 | - Subscription = Use the current subscription
193 | - Resource Group = Use Existing and select "myportal-rg"
194 | - Name = Must be a unique name, there will be a green checkmark that shows up in the text box if your name is available. Example "storageaccount"
195 | - Location = East US
196 | - Performance = Standard
197 | - Account Kind = Storage V2
198 | - Replication = LRS
199 | - Access Tier = Hot
200 |
201 | 
202 |
203 |
204 | Click "Review + Create" -> "Create".
205 |
206 | At this point we have a Resource Group and a Storage Account and are ready to import this into Terraform.
207 |
208 | 
209 |
210 | ### Create Terraform Configuration
211 |
212 | Your Azure Cloud Shell should still be in the folder for this challenge with a single `main.tf` file.
213 | We will now add `resource` blocks to represent the infrastructure we are about to import.
214 |
215 | We have two resources we need to import into our Terraform Configuration, to do this we need to do two things:
216 |
217 | 1. Create the base Terraform configuration for both resources.
218 | 2. Run `terraform import` to bring the infrastructure into our state file.
219 |
220 | To create the base configuration place the following code into the `main.tf` file.
221 |
222 | ```hcl
223 | resource "azurerm_resource_group" "import" {
224 | name = "myportal-rg"
225 | location = "eastus"
226 | }
227 |
228 | resource "azurerm_storage_account" "import" {
229 | name = "myusernamestorageaccount"
230 | resource_group_name = "${azurerm_resource_group.import.name}"
231 | location = "eastus"
232 | account_kind = "StorageV2"
233 | account_tier = "Standard"
234 | account_replication_type = "LRS"
235 | enable_https_traffic_only = true
236 | }
237 | ```
238 |
239 | `terraform plan`
240 |
241 | Shows 2 to add
242 |
243 | ```sh
244 | Terraform will perform the following actions:
245 |
246 | + azurerm_resource_group.main
247 | id:
248 | location: "centralus"
249 | name: "myportal-rg"
250 | tags.%:
251 |
252 | + azurerm_storage_account.main
253 | id:
254 | access_tier:
255 | account_encryption_source: "Microsoft.Storage"
256 | account_kind: "Storage"
257 | account_replication_type: "LRS"
258 | account_tier: "Standard"
259 | enable_blob_encryption:
260 | enable_file_encryption:
261 | location: "centralus"
262 | name: "myusernamestorageaccount"
263 | primary_access_key:
264 | primary_blob_connection_string:
265 | primary_blob_endpoint:
266 | primary_connection_string:
267 | primary_file_endpoint:
268 | primary_location:
269 | primary_queue_endpoint:
270 | primary_table_endpoint:
271 | resource_group_name: "myportal-rg"
272 | secondary_access_key:
273 | secondary_blob_connection_string:
274 | secondary_blob_endpoint:
275 | secondary_connection_string:
276 | secondary_location:
277 | secondary_queue_endpoint:
278 | secondary_table_endpoint:
279 | tags.%:
280 |
281 |
282 | Plan: 2 to add, 0 to change, 0 to destroy.
283 | ```
284 |
285 | > CAUTION: This is not what we want!
286 |
287 | ### Import the Resource Group
288 |
289 | We need two values to run the `terraform import` command:
290 |
291 | 1. Resource Address from our configuration
292 | 1. Azure Resource ID
293 |
294 | The Resource Address is simple enough, based on the configuration above it is simply "azurerm_resource_group.main".
295 |
296 | The Azure Resource ID can be retrieved using the Azure CLI by running `az group show -g myportal-rg --query id`. The value should look something like "/subscriptions/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myportal-rg".
297 |
298 | Now run the import command:
299 |
300 | ```sh
301 | $ terraform import azurerm_resource_group.import /subscriptions/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myportal-rg
302 |
303 | Import successful!
304 |
305 | The resources that were imported are shown above. These resources are now in
306 | your Terraform state and will henceforth be managed by Terraform.
307 | ```
308 |
309 | ### Import the Storage Account
310 |
311 | The process here is the same.
312 |
313 | The Resource Address is simple enough, based on the configuration above it is simply "azurerm_storage_account.main".
314 |
315 | The Azure Resource ID can be retrieved using the Azure CLI by running `az storage account show -g myportal-rg -n myusernamestorageaccount --query id`. The value should look something like "/subscriptions/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myportal-rg/providers/Microsoft.Storage/storageAccounts/myusernamestorageaccount".
316 |
317 | ```sh
318 | $ terraform import azurerm_storage_account.import /subscriptions/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myportal-rg/providers/Microsoft.Storage/storageAccounts/myusernamestorageaccount
319 |
320 | Import successful!
321 |
322 | The resources that were imported are shown above. These resources are now in
323 | your Terraform state and will henceforth be managed by Terraform.
324 | ```
325 |
326 | ### Verify Plan
327 |
328 | Run a `terraform plan`, you should see no changes:
329 |
330 | ```sh
331 | $ terraform plan
332 |
333 | ...
334 |
335 | No changes. Infrastructure is up-to-date.
336 |
337 | This means that Terraform did not detect any differences between your
338 | configuration and real physical resources that exist. As a result, no
339 | actions need to be performed.
340 | ```
341 |
342 | ### Make a Change
343 |
344 | Add the following tag configuration to both the Resource Group and the Storage Account:
345 |
346 | ```hcl
347 | resource "azurerm_resource_group" "import" {
348 | ...
349 | tags {
350 | terraform = "true"
351 | }
352 | }
353 |
354 | resource "azurerm_storage_account" "import" {
355 | ...
356 | tags {
357 | terraform = "true"
358 | }
359 | }
360 | ```
361 |
362 | Run a plan, we should see two changes.
363 |
364 | ```sh
365 | ~ azurerm_resource_group.import
366 | tags.%: "0" => "1"
367 | tags.terraform: "" => "true"
368 |
369 | ~ azurerm_storage_account.import
370 | tags.%: "0" => "1"
371 | tags.terraform: "" => "true"
372 |
373 |
374 | Plan: 0 to add, 2 to change, 0 to destroy.
375 | ```
376 |
377 | Run `terraform apply`.
378 |
379 | SUCCESS! You have now brought existing infrastructure into Terraform.
380 |
381 | ### Cleanup
382 |
383 | When you are done, destroy the infrastructure, you no longer need it.
384 |
385 | ```sh
386 | $ terraform destroy
387 | azurerm_resource_group.main: Refreshing state... (ID: /subscriptions/.../resourceGroups/challenge01-rg)
388 | azurerm_resource_group.import: Refreshing state... (ID: /subscriptions/.../resourceGroups/myportal-rg)
389 | azurerm_resource_group.count[0]: Refreshing state... (ID: /subscriptions/.../resourceGroups/challenge01-rg-0)
390 | azurerm_resource_group.count[1]: Refreshing state... (ID: /subscriptions/.../resourceGroups/challenge01-rg-1)
391 | azurerm_storage_account.import: Refreshing state... (ID: /subscriptions/.../storageAccounts/myusernamestorageaccount)
392 |
393 | An execution plan has been generated and is shown below.
394 | Resource actions are indicated with the following symbols:
395 | - destroy
396 |
397 | Terraform will perform the following actions:
398 |
399 | - azurerm_resource_group.count[0]
400 |
401 | - azurerm_resource_group.count[1]
402 |
403 | - azurerm_resource_group.import
404 |
405 | - azurerm_resource_group.main
406 |
407 | - azurerm_storage_account.import
408 |
409 |
410 | Plan: 0 to add, 0 to change, 5 to destroy.
411 |
412 | Do you really want to destroy all resources?
413 | Terraform will destroy all your managed infrastructure, as shown above.
414 | There is no undo. Only 'yes' will be accepted to confirm.clear
415 |
416 | Enter a value: yes
417 |
418 | azurerm_resource_group.count[1]: Destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg-1)
419 | azurerm_resource_group.main: Destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg)
420 | azurerm_storage_account.import: Destroying... (ID: /subscriptions/.../storageAccounts/myusernamestorageaccount)
421 | azurerm_resource_group.count[0]: Destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg-0)
422 | azurerm_storage_account.import: Destruction complete after 1s
423 | azurerm_resource_group.import: Destroying... (ID: /subscriptions/.../resourceGroups/myportal-rg)
424 | azurerm_resource_group.main: Still destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg, 10s elapsed)
425 | azurerm_resource_group.count.1: Still destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg-1, 10s elapsed)
426 | azurerm_resource_group.count.0: Still destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg-0, 10s elapsed)
427 | azurerm_resource_group.import: Still destroying... (ID: /subscriptions/.../resourceGroups/myportal-rg, 10s elapsed)
428 | azurerm_resource_group.main: Still destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg, 20s elapsed)
429 | azurerm_resource_group.count.1: Still destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg-1, 20s elapsed)
430 | azurerm_resource_group.count.0: Still destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg-0, 20s elapsed)
431 | azurerm_resource_group.import: Still destroying... (ID: /subscriptions/.../resourceGroups/myportal-rg, 20s elapsed)
432 | azurerm_resource_group.main: Still destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg, 30s elapsed)
433 | azurerm_resource_group.count.1: Still destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg-1, 30s elapsed)
434 | azurerm_resource_group.count.0: Still destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg-0, 30s elapsed)
435 | azurerm_resource_group.import: Still destroying... (ID: /subscriptions/.../resourceGroups/myportal-rg, 30s elapsed)
436 | azurerm_resource_group.main: Still destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg, 40s elapsed)
437 | azurerm_resource_group.count.1: Still destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg-1, 40s elapsed)
438 | azurerm_resource_group.count.0: Still destroying... (ID: /subscriptions/.../resourceGroups/challenge01-rg-0, 40s elapsed)
439 | azurerm_resource_group.import: Still destroying... (ID: /subscriptions/.../resourceGroups/myportal-rg, 40s elapsed)
440 | azurerm_resource_group.count[0]: Destruction complete after 45s
441 | azurerm_resource_group.count[1]: Destruction complete after 45s
442 | azurerm_resource_group.main: Destruction complete after 45s
443 | azurerm_resource_group.import: Destruction complete after 46s
444 |
445 | Destroy complete! Resources: 5 destroyed.
446 | ```
447 |
448 | Because the infrastructure is now managed by Terraform, we can destroy just like before.
449 |
450 | Run a `terraform destroy` and follow the prompts to remove the infrastructure.
451 |
452 | ## Advanced areas to explore
453 |
454 | 1. Play around with adjusting the `count` and `name` parameters, then running `plan` and `apply`.
455 | 2. Run the `plan` command with the `-out` option and apply that output.
456 | 3. Add tags to each resource.
457 |
458 | ## Resources
459 |
460 | - [Terraform Count](https://www.terraform.io/docs/configuration/interpolation.html#count-information)
461 | - [Terraform Import](https://www.terraform.io/docs/commands/import.html)
462 |
--------------------------------------------------------------------------------
/challenges/02-aci-helloworld/README.md:
--------------------------------------------------------------------------------
1 | # 02 - Azure Container Instance
2 |
3 | ## Expected Outcome
4 |
5 | In this challenge, you will learn how to deploy a PaaS solution using Terraform.
6 |
7 | There are two docker containers that will be deployed:
8 |
9 | - [Hello World](https://github.com/Azure-Samples/aci-helloworld)
10 | - A simple NodeJS web application that displays a message.
11 | - Hosted on [Dockerhub](https://hub.docker.com/r/microsoft/aci-helloworld/)
12 | - [Sidecar](https://github.com/Azure-Samples/aci-tutorial-sidecar)
13 | - A simple watchdog script that calls the Hello World application every 3 seconds.
14 | - Hosted on [Dockerhub](https://hub.docker.com/r/microsoft/aci-tutorial-sidecar/)
15 |
16 | ## How to
17 |
18 | ### Create Terraform Configuration
19 |
20 | From the Cloud Shell, change directory into a folder specific to this challenge. If you created the scaffolding in Challenge 00, then then you can use the command `cd ~/AzureWorkChallenges/challenge02/`.
21 |
22 | Create a new file called `main.tf` with the following contents:
23 |
24 | ```hcl
25 | resource "random_pet" "main" {
26 | length = 2
27 | separator = ""
28 | }
29 |
30 | resource "azurerm_resource_group" "main" {
31 | name = "aci-helloworld"
32 | location = "eastus"
33 | }
34 |
35 | resource "azurerm_storage_account" "main" {
36 | name = "acidev${random_pet.main.id}"
37 | resource_group_name = "${azurerm_resource_group.main.name}"
38 | location = "${azurerm_resource_group.main.location}"
39 | account_tier = "Standard"
40 | account_replication_type = "LRS"
41 | }
42 |
43 | resource "azurerm_storage_share" "main" {
44 | name = "aci-test-share"
45 | resource_group_name = "${azurerm_resource_group.main.name}"
46 | storage_account_name = "${azurerm_storage_account.main.name}"
47 | quota = 1
48 | }
49 |
50 | resource "azurerm_container_group" "main" {
51 | name = "aci-helloworld"
52 | location = "${azurerm_resource_group.main.location}"
53 | resource_group_name = "${azurerm_resource_group.main.name}"
54 | ip_address_type = "public"
55 | dns_name_label = "aci-${random_pet.main.id}"
56 | os_type = "linux"
57 |
58 | container {
59 | name = "helloworld"
60 | image = "microsoft/aci-helloworld"
61 | cpu = "0.5"
62 | memory = "1.5"
63 | port = "80"
64 |
65 | environment_variables {
66 | "NODE_ENV" = "testing"
67 | }
68 |
69 | volume {
70 | name = "logs"
71 | mount_path = "/aci/logs"
72 | read_only = false
73 | share_name = "${azurerm_storage_share.main.name}"
74 |
75 | storage_account_name = "${azurerm_storage_account.main.name}"
76 | storage_account_key = "${azurerm_storage_account.main.primary_access_key}"
77 | }
78 | }
79 |
80 | container {
81 | name = "sidecar"
82 | image = "microsoft/aci-tutorial-sidecar"
83 | cpu = "0.5"
84 | memory = "1.5"
85 | }
86 |
87 | tags {
88 | environment = "testing"
89 | }
90 | }
91 | ```
92 |
93 | ### Terraform Init and Plan
94 |
95 | Running an `init` should look something like this:
96 |
97 | ```sh
98 | terraform init
99 |
100 | ...
101 |
102 | Terraform has been successfully initialized!
103 | ```
104 |
105 | Running a `plan` should look something like this:
106 |
107 | ```sh
108 | terraform plan
109 | Terraform will perform the following actions:
110 |
111 | + azurerm_container_group.main
112 | name: "aci-helloworld"
113 | ...
114 |
115 | + azurerm_resource_group.main
116 | name: "aci-helloworld"
117 | ...
118 |
119 | + azurerm_storage_account.main
120 | name: ""
121 | ...
122 |
123 | + azurerm_storage_share.main
124 | name: "aci-test-share"
125 | ...
126 |
127 |
128 | Plan: 4 to add, 0 to change, 0 to destroy.
129 | ```
130 |
131 | ### Terraform Apply
132 |
133 | Running an `apply` should look just like a plan except you are prompted for approval to apply.
134 | Type 'yes' and let Terraform build your infrastructure.
135 |
136 | ### Navigate to the Azure Portal
137 |
138 | Open a browser and navigate to the the [Azure Portal](https://portal.azure.com) and you should see your resource group and its resources.
139 | 
140 |
141 | ### Find the Full Qualified Domain Name
142 |
143 | Click into the Azure Container Instance and take note of its FQDN.
144 | 
145 |
146 | ### Navigate to the Web App
147 |
148 | Navigate to that URL and you should see the following:
149 | 
150 |
151 | ### View the Logs
152 | Back in the Azure Portal, navigate to the Azure Container Instance and view its logs by clicking on the "Containers" tab:
153 | 
154 |
155 | Wait a few seconds and refresh the logs, you should see more requests due to the sidecar container.
156 |
157 | ## A Step Further (optional)
158 |
159 | Azure Container Instances also support windows containers!
160 |
161 | ### Create a Windows Container
162 |
163 | Create another resource by adding the following to your existing `main.tf` file:
164 |
165 | ```hcl
166 | resource "azurerm_container_group" "windows" {
167 | name = "aci-iis"
168 | location = "${azurerm_resource_group.main.location}"
169 | resource_group_name = "${azurerm_resource_group.main.name}"
170 | ip_address_type = "public"
171 | dns_name_label = "aci-iis-${random_pet.main.id}"
172 | os_type = "windows"
173 |
174 | container {
175 | name = "dotnetsample"
176 | image = "microsoft/iis"
177 | cpu = "0.5"
178 | memory = "1.5"
179 | port = "80"
180 | }
181 |
182 | tags {
183 | environment = "testing"
184 | }
185 | }
186 | ```
187 |
188 | ### Run Terraform Workflow
189 |
190 | Running an `init`, `plan`, and `apply` should yield another Container Instance.
191 |
192 | Navigating back to the Azure Portal to get the FQDN and following that URL should get you the very familiar IIS default sites page:
193 | 
194 |
195 | ## Cleanup
196 |
197 | Run a `terraform destroy` when you are done exploring.
198 |
199 | ## Advanced areas to explore
200 |
201 | 1. What do you think will happen if you try to combine the Azure Container Instances above (Linux and Windows) into one?
202 | 2. Replicate the Terraform above using a single Azure CLI command. Which is easier?
203 |
204 | ## Resources
205 |
206 | - [Azurerm Container Group Docs](https://www.terraform.io/docs/providers/azurerm/r/container_group.html)
207 | - [Azure Container Instances](https://azure.microsoft.com/en-us/services/container-instances)
208 |
--------------------------------------------------------------------------------
/challenges/03-azurevm/README.md:
--------------------------------------------------------------------------------
1 | # 03 - Azure Virtual Machine
2 |
3 | ## Expected Outcome
4 |
5 | In this challenge, you will create a Azure Virtual Machine running Windows Server.
6 |
7 | You will gradually add Terraform configuration to build all the resources needed to be able to login to the Azure Virtual Machine.
8 |
9 | The resources you will use in this challenge:
10 |
11 | - Resource Group
12 | - Virtual Network
13 | - Subnet
14 | - Network Interface
15 | - Virtual Machine
16 | - Public IP Address
17 |
18 | ## How to
19 |
20 | ### Create the base Terraform Configuration
21 |
22 | We will start with a few of the basic resources needed.
23 |
24 | From the Cloud Shell, change directory into a folder specific to this challenge. If you created the scaffolding in Challenge 00, then then you can use the command `cd ~/AzureWorkChallenges/challenge03/`.
25 |
26 | Create a `main.tf` file to hold our configuration.
27 |
28 | ### Create Variables
29 |
30 | Create a few variables that will help keep our code clean:
31 |
32 | ```hcl
33 | variable "name" {
34 | default = "challenge03"
35 | }
36 |
37 | variable "location" {
38 | default = "eastus"
39 | }
40 | ```
41 |
42 | ### Create a Resource Group
43 |
44 | Now create a Resource Group to contain all of our infrastructure using the variables to interpolate the parameters:
45 |
46 | ```hcl
47 | resource "azurerm_resource_group" "main" {
48 | name = "${var.name}-rg"
49 | location = "${var.location}"
50 | }
51 | ```
52 |
53 | ### Create Virtual Networking
54 |
55 | In order to create an Azure Virtual Machine we need to create a network in which to place it.
56 |
57 | Create a Virtual Network and Subnet using a basic CIDR block to allocate an IP block:
58 |
59 | ```hcl
60 | resource "azurerm_virtual_network" "main" {
61 | name = "${var.name}-vnet"
62 | address_space = ["10.0.0.0/16"]
63 | location = "${azurerm_resource_group.main.location}"
64 | resource_group_name = "${azurerm_resource_group.main.name}"
65 | }
66 |
67 | resource "azurerm_subnet" "main" {
68 | name = "${var.name}-subnet"
69 | resource_group_name = "${azurerm_resource_group.main.name}"
70 | virtual_network_name = "${azurerm_virtual_network.main.name}"
71 | address_prefix = "10.0.1.0/24"
72 | }
73 | ```
74 |
75 | > Notice that we use the available metadata from the `azurerm_resource_group.main` resource to populate the parameters of other resources.
76 |
77 | ### Run Terraform Workflow
78 |
79 | Run `terraform init` since this is the first time we are running Terraform from this directory.
80 |
81 | Run `terraform plan` where you should see the plan of two new resources, namely the Resource Group and the Virtual Network.
82 |
83 | View Output
84 |
85 |
86 | ```sh
87 | $ terraform plan
88 | Refreshing Terraform state in-memory prior to plan...
89 | The refreshed state will be used to calculate this plan, but will not be
90 | persisted to local or remote state storage.
91 |
92 |
93 | ------------------------------------------------------------------------
94 |
95 | An execution plan has been generated and is shown below.
96 | Resource actions are indicated with the following symbols:
97 | + create
98 |
99 | Terraform will perform the following actions:
100 |
101 | + azurerm_resource_group.main
102 | id:
103 | location: "eastus"
104 | name: "challenge03-rg"
105 | tags.%:
106 |
107 | + azurerm_subnet.main
108 | id:
109 | address_prefix: "10.0.1.0/24"
110 | ip_configurations.#:
111 | name: "challenge03-subnet"
112 | resource_group_name: "challenge03-rg"
113 | virtual_network_name: "challenge03-vnet"
114 |
115 | + azurerm_virtual_network.main
116 | id:
117 | address_space.#: "1"
118 | address_space.0: "10.0.0.0/16"
119 | location: "eastus"
120 | name: "challenge03-vnet"
121 | resource_group_name: "challenge03-rg"
122 | subnet.#:
123 | tags.%:
124 |
125 |
126 | Plan: 3 to add, 0 to change, 0 to destroy.
127 |
128 | ------------------------------------------------------------------------
129 |
130 | Note: You didn't specify an "-out" parameter to save this plan, so Terraform
131 | can't guarantee that exactly these actions will be performed if
132 | "terraform apply" is subsequently run.
133 | ```
134 |
135 |
136 |
137 |
138 | If your plan looks good, go ahead and run `terraform apply` and type "yes" to confirm you want to apply.
139 | When it completes you should see:
140 |
141 | ```sh
142 | Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
143 | ```
144 |
145 | ### Create the Azure Virtual Machine
146 |
147 | Now that we have base networking in place, we will add a Network Interface and Virtual Machine.
148 | We will create a VM with an Azure Marketplace Image for Windows Server 2016 Datacenter.
149 |
150 | Create the Network Interface resource:
151 |
152 | ```hcl
153 | resource "azurerm_network_interface" "main" {
154 | name = "${var.name}-nic"
155 | location = "${azurerm_resource_group.main.location}"
156 | resource_group_name = "${azurerm_resource_group.main.name}"
157 |
158 | ip_configuration {
159 | name = "config1"
160 | subnet_id = "${azurerm_subnet.main.id}"
161 | private_ip_address_allocation = "dynamic"
162 | }
163 | }
164 | ```
165 |
166 | Create the Virtual Machine resource:
167 |
168 | ```hcl
169 | resource "azurerm_virtual_machine" "main" {
170 | name = "${var.name}-vm"
171 | location = "${azurerm_resource_group.main.location}"
172 | resource_group_name = "${azurerm_resource_group.main.name}"
173 | network_interface_ids = ["${azurerm_network_interface.main.id}"]
174 | vm_size = "Standard_A2_v2"
175 |
176 | storage_image_reference {
177 | publisher = "MicrosoftWindowsServer"
178 | offer = "WindowsServer"
179 | sku = "2016-Datacenter"
180 | version = "latest"
181 | }
182 |
183 | storage_os_disk {
184 | name = "${var.name}vm-osdisk"
185 | caching = "ReadWrite"
186 | create_option = "FromImage"
187 | managed_disk_type = "Standard_LRS"
188 | }
189 |
190 | os_profile {
191 | computer_name = "${var.name}vm"
192 | admin_username = "testadmin"
193 | admin_password = "Password1234!"
194 | }
195 |
196 | os_profile_windows_config {}
197 | }
198 | ```
199 |
200 | Take note of the OS image:
201 |
202 | ```hcl
203 | storage_image_reference {
204 | publisher = "MicrosoftWindowsServer"
205 | offer = "WindowsServer"
206 | sku = "2016-Datacenter"
207 | version = "latest"
208 | }
209 | ```
210 |
211 | Run a plan and apply to create both these resources.
212 |
213 | ### Add a Public IP
214 |
215 | At this point you should have a running Virtual Machine in Azure running Windows Server, however you have no way to access it. To do this we must do two things, create the Public IP Resource and configure the Network Interface to use it.
216 |
217 | Create a Public IP Address that will assign an IP address:
218 |
219 | ```hcl
220 | resource "azurerm_public_ip" "main" {
221 | name = "${var.name}-pubip"
222 | location = "${azurerm_resource_group.main.location}"
223 | resource_group_name = "${azurerm_resource_group.main.name}"
224 | allocation_method = "Static"
225 | }
226 | ```
227 |
228 | Update the IP Configuration parameter of the Network Interface to attach the Public IP:
229 |
230 | ```hcl
231 | resource "azurerm_network_interface" "main" {
232 | ...
233 |
234 | ip_configuration {
235 | ...
236 | public_ip_address_id = "${azurerm_public_ip.main.id}"
237 | }
238 | }
239 | ```
240 |
241 | ### Terraform Plan
242 |
243 | Running `terraform plan` should contain something like the following:
244 |
245 | ```sh
246 | ~ azurerm_network_interface.main
247 | ip_configuration.0.public_ip_address_id: "" => "${azurerm_public_ip.main.id}"
248 |
249 | + azurerm_public_ip.main
250 | id:
251 | fqdn:
252 | ip_address:
253 | location: "eastus"
254 | name: "challenge03-pubip"
255 | public_ip_address_allocation: "static"
256 | resource_group_name: "challenge03-rg"
257 | sku: "Basic"
258 | tags.%:
259 |
260 |
261 | Plan: 1 to add, 1 to change, 0 to destroy.
262 | ```
263 |
264 | > Notice that there is a new resource being added and one being updated.
265 |
266 | Run `terraform apply` to apply the changes.
267 |
268 | ### Outputs
269 |
270 | You now have all the infrastructure in place and can now Remote Desktop into the Windows Server VM we just stood up.
271 |
272 | But wait, the Public IP was dynamically created, how do I access it?
273 |
274 | You could check the value in the Azure Portal, however let's instead add an output to get that information.
275 |
276 | Add the following output:
277 |
278 | ```hcl
279 | output "private-ip" {
280 | value = "${azurerm_network_interface.main.private_ip_address}"
281 | description = "Private IP Address"
282 | }
283 |
284 | output "public-ip" {
285 | value = "${azurerm_public_ip.main.ip_address}"
286 | description = "Public IP Address"
287 | }
288 | ```
289 |
290 | Now run a `terraform refresh`, which will refresh your state file with the real-world infrastructure and resolve the new outputs you just created.
291 |
292 | ```sh
293 | $ terraform refresh
294 | azurerm_resource_group.main: Refreshing state... (ID: /subscriptions/.../resourceGroups/challenge03-rg)
295 | azurerm_virtual_network.main: Refreshing state... (ID: /subscriptions/.../virtualNetworks/challenge03-vnet)
296 | azurerm_public_ip.main: Refreshing state... (ID: /subscriptions/.../publicIPAddresses/challenge03-pubip)
297 | azurerm_subnet.main: Refreshing state... (ID: /subscriptions/.../subnets/challenge03-subnet)
298 | azurerm_network_interface.main: Refreshing state... (ID: /subscriptions/.../networkInterfaces/challenge03-nic)
299 | azurerm_virtual_machine.main: Refreshing state... (ID: /subscriptions/.../virtualMachines/challenge03-vm)
300 |
301 | Outputs:
302 |
303 | private-ip = 10.0.1.4
304 | public-ip = 168.61.55.117
305 | ```
306 |
307 | > Note: you can also run `terraform output` to see just these outputs without having to run refresh again.
308 |
309 | ### Remote Desktop (optional)
310 |
311 | Using the Public IP output value, Remote Desktop into the Virtual Machine to verify connectivity.
312 |
313 | 
314 |
315 | Success! You have now stood up a Virtual Machine in Azure using Terraform!
316 |
317 | ### Clean up
318 |
319 | When you are done, run `terraform destroy` to remove everything we created:
320 |
321 | ```sh
322 | terraform destroy
323 | azurerm_resource_group.main: Refreshing state... (ID: /subscriptions/.../resourceGroups/challenge03-rg)
324 | azurerm_public_ip.main: Refreshing state... (ID: /subscriptions/.../publicIPAddresses/challenge03-pubip)
325 | azurerm_virtual_network.main: Refreshing state... (ID: /subscriptions/.../virtualNetworks/challenge03-vnet)
326 | azurerm_subnet.main: Refreshing state... (ID: /subscriptions/.../subnets/challenge03-subnet)
327 | azurerm_network_interface.main: Refreshing state... (ID: /subscriptions/.../networkInterfaces/challenge03-nic)
328 | azurerm_virtual_machine.main: Refreshing state... (ID: /subscriptions/.../virtualMachines/challenge03-vm)
329 |
330 | An execution plan has been generated and is shown below.
331 | Resource actions are indicated with the following symbols:
332 | - destroy
333 |
334 | Terraform will perform the following actions:
335 |
336 | - azurerm_network_interface.main
337 |
338 | - azurerm_public_ip.main
339 |
340 | - azurerm_resource_group.main
341 |
342 | - azurerm_subnet.main
343 |
344 | - azurerm_virtual_machine.main
345 |
346 | - azurerm_virtual_network.main
347 |
348 |
349 | Plan: 0 to add, 0 to change, 6 to destroy.
350 |
351 | Do you really want to destroy?
352 | Terraform will destroy all your managed infrastructure, as shown above.
353 | There is no undo. Only 'yes' will be accepted to confirm.
354 | ...
355 | ```
356 |
357 | ## Advanced areas to explore
358 |
359 | 1. Extract secrets into required variables.
360 | 1. Add a data disk.
361 | 1. Add a DNS Label to the Public IP Address.
362 | 1. Search for Marketplace Images. (hint: use the Azurel CLI and start with `az vm image -h`)
363 |
364 | ## Resources
365 |
366 | - [Azure Resource Group](https://www.terraform.io/docs/providers/azurerm/r/resource_group.html)
367 | - [Azure Virtual Network](https://www.terraform.io/docs/providers/azurerm/r/virtual_network.html)
368 | - [Azure Subnet](https://www.terraform.io/docs/providers/azurerm/r/subnet.html)
369 | - [Azure Network Interface](https://www.terraform.io/docs/providers/azurerm/r/network_interface.html)
370 | - [Azure Virtual Machine](https://www.terraform.io/docs/providers/azurerm/r/virtual_machine.html)
371 | - [Public IP Address](https://www.terraform.io/docs/providers/azurerm/r/public_ip.html)
372 |
--------------------------------------------------------------------------------
/challenges/04-terraformcount/README.md:
--------------------------------------------------------------------------------
1 | # 04 - Terraform Count
2 |
3 | ## Expected Outcome
4 |
5 | In this challenge, you will take what you did in Challenge 03 and expand to take a count variable.
6 |
7 | Be aware that if you did not destroy the infrastructure from Challenge 03 you may run into resource naming conflicts (namely "Resource already exists").
8 |
9 | ## How to
10 |
11 | ### Copy Terraform Configuration
12 |
13 | From the Cloud Shell, change directory into a folder specific to this challenge. If you created the scaffolding in Challenge 00, then then you can use the command `cd ~/AzureWorkChallenges/challenge04/`.
14 |
15 | Copy the `main.tf` file from challenge 03 into the current directory.
16 |
17 | Be sure to update the value of the `name` variable:
18 |
19 | ```hcl
20 | variable "name" {
21 | default = "challenge04"
22 | }
23 | ```
24 |
25 | ### Add a count variable
26 |
27 | Create a new variable called `vmcount` and default it to `1`.
28 |
29 | ```hcl
30 | variable "vmcount" {
31 | default = 1
32 | }
33 | ```
34 |
35 | ### Update Existing Resources
36 |
37 | Not every resource needs to scale as the number of VM's increase.
38 |
39 | The Resource Group, Virtual Network and Subnet will not change.
40 |
41 | We will have to scale the Network Interface, Public IP, and VM resources.
42 |
43 | Each of these resources you will need make these changes:
44 |
45 | - Add the count variable
46 |
47 | ```hcl
48 | count = "${var.vmcount}"
49 | ```
50 |
51 | - Update the resource name to include the count index, for example the VM resource:
52 |
53 | ```hcl
54 | name = "${var.name}-vm${count.index}"
55 | ```
56 |
57 | - Update the OS Disk Name on the VM resource:
58 |
59 | ```hcl
60 | storage_os_disk {
61 | name = "${var.name}vm${count.index}-osdisk"
62 | ...
63 | }
64 | ```
65 |
66 | - Update the computer name on the VM resource.
67 |
68 | ```hcl
69 | os_profile {
70 | computer_name = "${var.name}vm${count.index}"
71 | ...
72 | }
73 | ```
74 |
75 | - Update the ID reference for the Virtual Machine:
76 |
77 | ```hcl
78 | network_interface_ids = ["${element(azurerm_network_interface.main.*.id, count.index)}"]
79 | ```
80 |
81 | - Update the Public IP block:
82 |
83 | ```hcl
84 | resource "azurerm_public_ip" "main" {
85 | name = "${var.name}-pubip${count.index}"
86 | ...
87 | count = "${var.vmcount}"
88 | }
89 | ```
90 |
91 | - Update the Public IP ID reference for the Network Interface:
92 |
93 | ```hcl
94 | public_ip_address_id = "${element(azurerm_public_ip.main.*.id, count.index)}"
95 | ```
96 |
97 | - Update the Private IP outputs to display an array of IPs:
98 |
99 | ```hcl
100 | value = "${azurerm_network_interface.main.*.private_ip_address}"
101 | ```
102 |
103 | - Update the Public IP outputs to display an array of IPs:
104 |
105 | ```hcl
106 | value = "${azurerm_public_ip.main.*.ip_address}"
107 | ```
108 |
109 | ### Run a plan
110 |
111 | If all the changes above have been made without error, runnning `terraform init` and `terraform plan` should execute without any errors.
112 |
113 | The plan should end with:
114 |
115 | ```sh
116 | Plan: 6 to add, 0 to change, 0 to destroy.
117 | ```
118 |
119 | Now investigate this plan in more detail and you will notice that names have been set using the count index:
120 |
121 | ```sh
122 | ...
123 | + azurerm_virtual_machine.module[0]
124 | ...
125 | name: "challenge04-vm0"
126 | ...
127 | os_profile.3613624746.computer_name: "challenge04vm0"
128 | ...
129 |
130 | ```
131 |
132 | ### Update Count
133 |
134 | Set the default value of the count to `2`.
135 | Before running a plan consider the following questions:
136 |
137 | - How many resources do expect the plan to show?
138 | - What will the outputs look like?
139 |
140 | Your plan should look similar to the following
141 | View Output
142 |
143 |
144 | ```sh
145 | $ terraform plan
146 | Refreshing Terraform state in-memory prior to plan...
147 | The refreshed state will be used to calculate this plan, but will not be
148 | persisted to local or remote state storage.
149 |
150 |
151 | ------------------------------------------------------------------------
152 |
153 | An execution plan has been generated and is shown below.
154 | Resource actions are indicated with the following symbols:
155 | + create
156 |
157 | Terraform will perform the following actions:
158 |
159 | + azurerm_network_interface.main[0]
160 | id:
161 | applied_dns_servers.#:
162 | dns_servers.#:
163 | enable_accelerated_networking: "false"
164 | enable_ip_forwarding: "false"
165 | internal_dns_name_label:
166 | internal_fqdn:
167 | ip_configuration.#: "1"
168 | ip_configuration.0.application_gateway_backend_address_pools_ids.#:
169 | ip_configuration.0.application_security_group_ids.#:
170 | ip_configuration.0.load_balancer_backend_address_pools_ids.#:
171 | ip_configuration.0.load_balancer_inbound_nat_rules_ids.#:
172 | ip_configuration.0.name: "config1"
173 | ip_configuration.0.primary:
174 | ip_configuration.0.private_ip_address:
175 | ip_configuration.0.private_ip_address_allocation: "dynamic"
176 | ip_configuration.0.public_ip_address_id: "${element(azurerm_public_ip.main.*.id, count.index)}"
177 | ip_configuration.0.subnet_id: "${azurerm_subnet.main.id}"
178 | location: "eastus"
179 | mac_address:
180 | name: "challenge04-nic0"
181 | private_ip_address:
182 | private_ip_addresses.#:
183 | resource_group_name: "challenge04-rg"
184 | tags.%:
185 | virtual_machine_id:
186 |
187 | + azurerm_network_interface.main[1]
188 | id:
189 | applied_dns_servers.#:
190 | dns_servers.#:
191 | enable_accelerated_networking: "false"
192 | enable_ip_forwarding: "false"
193 | internal_dns_name_label:
194 | internal_fqdn:
195 | ip_configuration.#: "1"
196 | ip_configuration.0.application_gateway_backend_address_pools_ids.#:
197 | ip_configuration.0.application_security_group_ids.#:
198 | ip_configuration.0.load_balancer_backend_address_pools_ids.#:
199 | ip_configuration.0.load_balancer_inbound_nat_rules_ids.#:
200 | ip_configuration.0.name: "config1"
201 | ip_configuration.0.primary:
202 | ip_configuration.0.private_ip_address:
203 | ip_configuration.0.private_ip_address_allocation: "dynamic"
204 | ip_configuration.0.public_ip_address_id: "${element(azurerm_public_ip.main.*.id, count.index)}"
205 | ip_configuration.0.subnet_id: "${azurerm_subnet.main.id}"
206 | location: "eastus"
207 | mac_address:
208 | name: "challenge04-nic1"
209 | private_ip_address:
210 | private_ip_addresses.#:
211 | resource_group_name: "challenge04-rg"
212 | tags.%:
213 | virtual_machine_id:
214 |
215 | + azurerm_public_ip.main[0]
216 | id:
217 | fqdn:
218 | ip_address:
219 | location: "eastus"
220 | name: "challenge04-pubip0"
221 | public_ip_address_allocation: "static"
222 | resource_group_name: "challenge04-rg"
223 | sku: "Basic"
224 | tags.%:
225 |
226 | + azurerm_public_ip.main[1]
227 | id:
228 | fqdn:
229 | ip_address:
230 | location: "eastus"
231 | name: "challenge04-pubip1"
232 | public_ip_address_allocation: "static"
233 | resource_group_name: "challenge04-rg"
234 | sku: "Basic"
235 | tags.%:
236 |
237 | + azurerm_resource_group.main
238 | id:
239 | location: "eastus"
240 | name: "challenge04-rg"
241 | tags.%:
242 |
243 | + azurerm_subnet.main
244 | id:
245 | address_prefix: "10.0.1.0/24"
246 | ip_configurations.#:
247 | name: "challenge04-subnet"
248 | resource_group_name: "challenge04-rg"
249 | virtual_network_name: "challenge04-vnet"
250 |
251 | + azurerm_virtual_machine.main[0]
252 | id:
253 | availability_set_id:
254 | delete_data_disks_on_termination: "false"
255 | delete_os_disk_on_termination: "false"
256 | identity.#:
257 | location: "eastus"
258 | name: "challenge04-vm0"
259 | network_interface_ids.#:
260 | os_profile.#: "1"
261 | os_profile.1750279281.admin_password:
262 | os_profile.1750279281.admin_username: "testadmin"
263 | os_profile.1750279281.computer_name: "challenge04vm0"
264 | os_profile.1750279281.custom_data:
265 | os_profile_windows_config.#: "1"
266 | os_profile_windows_config.429474957.additional_unattend_config.#: "0"
267 | os_profile_windows_config.429474957.enable_automatic_upgrades: "false"
268 | os_profile_windows_config.429474957.provision_vm_agent: "false"
269 | os_profile_windows_config.429474957.winrm.#: "0"
270 | resource_group_name: "challenge04-rg"
271 | storage_image_reference.#: "1"
272 | storage_image_reference.3904372903.id: ""
273 | storage_image_reference.3904372903.offer: "WindowsServer"
274 | storage_image_reference.3904372903.publisher: "MicrosoftWindowsServer"
275 | storage_image_reference.3904372903.sku: "2016-Datacenter"
276 | storage_image_reference.3904372903.version: "latest"
277 | storage_os_disk.#: "1"
278 | storage_os_disk.0.caching: "ReadWrite"
279 | storage_os_disk.0.create_option: "FromImage"
280 | storage_os_disk.0.disk_size_gb:
281 | storage_os_disk.0.managed_disk_id:
282 | storage_os_disk.0.managed_disk_type: "Standard_LRS"
283 | storage_os_disk.0.name: "challenge04vm0-osdisk"
284 | tags.%:
285 | vm_size: "Standard_A2_v2"
286 |
287 | + azurerm_virtual_machine.main[1]
288 | id:
289 | availability_set_id:
290 | delete_data_disks_on_termination: "false"
291 | delete_os_disk_on_termination: "false"
292 | identity.#:
293 | location: "eastus"
294 | name: "challenge04-vm1"
295 | network_interface_ids.#:
296 | os_profile.#: "1"
297 | os_profile.1900549424.admin_password:
298 | os_profile.1900549424.admin_username: "testadmin"
299 | os_profile.1900549424.computer_name: "challenge04vm1"
300 | os_profile.1900549424.custom_data:
301 | os_profile_windows_config.#: "1"
302 | os_profile_windows_config.429474957.additional_unattend_config.#: "0"
303 | os_profile_windows_config.429474957.enable_automatic_upgrades: "false"
304 | os_profile_windows_config.429474957.provision_vm_agent: "false"
305 | os_profile_windows_config.429474957.winrm.#: "0"
306 | resource_group_name: "challenge04-rg"
307 | storage_image_reference.#: "1"
308 | storage_image_reference.3904372903.id: ""
309 | storage_image_reference.3904372903.offer: "WindowsServer"
310 | storage_image_reference.3904372903.publisher: "MicrosoftWindowsServer"
311 | storage_image_reference.3904372903.sku: "2016-Datacenter"
312 | storage_image_reference.3904372903.version: "latest"
313 | storage_os_disk.#: "1"
314 | storage_os_disk.0.caching: "ReadWrite"
315 | storage_os_disk.0.create_option: "FromImage"
316 | storage_os_disk.0.disk_size_gb:
317 | storage_os_disk.0.managed_disk_id:
318 | storage_os_disk.0.managed_disk_type: "Standard_LRS"
319 | storage_os_disk.0.name: "challenge04vm1-osdisk"
320 | tags.%:
321 | vm_size: "Standard_A2_v2"
322 |
323 | + azurerm_virtual_network.main
324 | id:
325 | address_space.#: "1"
326 | address_space.0: "10.0.0.0/16"
327 | location: "eastus"
328 | name: "challenge04-vnet"
329 | resource_group_name: "challenge04-rg"
330 | subnet.#:
331 | tags.%:
332 |
333 |
334 | Plan: 9 to add, 0 to change, 0 to destroy.
335 |
336 | ------------------------------------------------------------------------
337 |
338 | Note: You didn't specify an "-out" parameter to save this plan, so Terraform
339 | can't guarantee that exactly these actions will be performed if
340 | "terraform apply" is subsequently run.
341 | ```
342 |
343 |
344 |
345 |
346 | Run `terraform apply` to create all the infrastructure.
347 |
348 | ### Azure Portal
349 |
350 | In the Azure Portal, view all the resources in the `challenge04-rg` Resource Group.
351 |
352 | How important do think naming is?
353 |
354 | How can Terraform help with keeping things consistent?
355 |
356 | ### Clean up
357 |
358 | Run `terraform destroy` to remove everything we created.
359 |
360 | ## Advanced areas to explore
361 |
362 | 1. Add an Azure Load Balancer.
363 | 2. Add tags to the Virtual Machine and then use the `-target` option to target only a single resource.
364 |
365 | ## Resources
366 |
367 | - [Azure Load Balancer](https://www.terraform.io/docs/providers/azurerm/r/loadbalancer.html)
368 | - [Resource Targeting](https://www.terraform.io/docs/commands/plan.html#resource-targeting)
369 |
--------------------------------------------------------------------------------
/challenges/05-terraformmodules/README.md:
--------------------------------------------------------------------------------
1 | # 05 - Terraform Modules
2 |
3 | ## Expected Outcome
4 |
5 | In this challenge, you will create a module to contain a scalable virtual machine deployment, then create an environment where you will call the module.
6 |
7 | ## How to
8 |
9 | ### Create Folder Structure
10 |
11 | From the Cloud Shell, change directory into a folder specific to this challenge. If you created the scaffolding in Challenge 00, then then you can use the command `cd ~/AzureWorkChallenges/challenge05/`.
12 |
13 | In order to organize your code, create the following folder structure with `main.tf` files.
14 |
15 | ```sh
16 | ├── environments
17 | │ └── dev
18 | │ └── main.tf
19 | └── modules
20 | └── my_virtual_machine
21 | └── main.tf
22 | ```
23 |
24 | ### Create the Module
25 |
26 | Inside the `my_virtual_machine` module folder copy over the terraform configuration from challenge 04.
27 |
28 | ### Create Variables
29 |
30 | Extract name, vm size, username and password into variables without defaults.
31 |
32 | This will result in them being required.
33 |
34 | ```hcl
35 | variable "name" {}
36 | variable "vm_size" {}
37 | variable "username" {}
38 | variable "password" {}
39 | ```
40 |
41 | > Extra credit: How many other variables can you extract?
42 |
43 | ### Create the Environment
44 |
45 | Change your working directory to the `environments/dev` folder.
46 |
47 | Update main.tf to declare your module, it could look similar to this:
48 |
49 | ```hcl
50 | variable "username" {}
51 | variable "password" {}
52 |
53 | module "myawesomewindowsvm" {
54 | source = "../../modules/my_virtual_machine"
55 | name = "awesomeapp"
56 | }
57 | ```
58 |
59 | > Notice the relative module sourcing.
60 |
61 | ### Terraform Init
62 |
63 | Run `terraform init`.
64 |
65 | ```sh
66 | Initializing modules...
67 | - module.myawesomewindowsvm
68 | Getting source "../../modules/my_virtual_machine"
69 |
70 | Error: module "myawesomewindowsvm": missing required argument "name"
71 | Error: module "myawesomewindowsvm": missing required argument "vm_size"
72 | Error: module "myawesomewindowsvm": missing required argument "username"
73 | Error: module "myawesomewindowsvm": missing required argument "password"
74 | ```
75 |
76 | We have a problem! We didn't set required variables for our module.
77 |
78 | Update the `main.tf` file:
79 |
80 | ```hcl
81 | module "myawesomewindowsvm" {
82 | source = "../../modules/my_virtual_machine"
83 | name = "awesomeapp"
84 | vm_size = "Standard_A2_v2"
85 | username = "${var.username}"
86 | password = "${var.password}"
87 | }
88 | ```
89 |
90 | Run `terraform init` again, this time there should not be any errors.
91 |
92 | ## Terraform Plan
93 |
94 | Run `terraform plan` and you should see your linux VM built from your module.
95 |
96 | ```sh
97 | + module.myawesomewindowsvm.azurerm_resource_group.module
98 | id:
99 | location: "centralus"
100 | name: "awesomeapp-rg"
101 |
102 | ...
103 |
104 | Plan: 6 to add, 0 to change, 0 to destroy.
105 | ```
106 |
107 | ## Add Another Module
108 |
109 | Add another `module` block describing another set of Virtual Machines:
110 |
111 | ```hcl
112 | module "differentwindowsvm" {
113 | source = "../../modules/my_virtual_machine"
114 | name = "differentapp"
115 | vm_size = "Standard_A2_v2"
116 | username = "${var.username}"
117 | password = "${var.password}"
118 | }
119 | ```
120 |
121 | ## Scale a single module
122 |
123 | Set the count of your first module to 2 and rerun a plan.
124 |
125 | ```hcl
126 | ...
127 | vmcount = 2
128 | ...
129 | ```
130 |
131 | Run a plan and observer that your first module can scale independently of the second one.
132 |
133 | ## Terraform Plan
134 |
135 | Since we added another module call, we must run `terraform init` again before running `terraform plan`.
136 |
137 | We should see twice as much infrastructure in our plan.
138 |
139 | ```sh
140 | + module.myawesomewindowsvm.azurerm_resource_group.module
141 | id:
142 | location: "centralus"
143 | name: "awesomeapp-rg"
144 |
145 | ...
146 |
147 | + module.differentlinuxvm.azurerm_resource_group.module
148 | id:
149 | location: "centralus"
150 | name: "differentapp-rg"
151 |
152 | ...
153 |
154 | Plan: 12 to add, 0 to change, 0 to destroy.
155 |
156 | ```
157 |
158 | ## More Variables
159 |
160 | In your `environments/dev/main.tf` file we can see some duplication and secrets we do not want to store in configuration.
161 |
162 | Add two variables to your environment `main.tf` file for username and password.
163 |
164 | Create a new file and name it `terraform.tfvars` that will contain our secrets and automatically loaded when we run a `plan`.
165 |
166 | ```hcl
167 | username = "testadmin"
168 | password = "Password1234!"
169 | ```
170 |
171 | ## Terraform Plan
172 |
173 | Run `terraform plan` and verify that your plan succeeds and looks the same.
174 |
175 | ## Advanced areas to explore
176 |
177 | 1. Use environment variables to load your secrets.
178 | 1. Add a reference to the Public Terraform Module for [Azure Compute](https://registry.terraform.io/modules/Azure/compute/azurerm)
179 |
180 | ## Resources
181 |
182 | - [Using Terraform Modules](https://www.terraform.io/docs/modules/usage.html)
183 | - [Source Terraform Modiules](https://www.terraform.io/docs/modules/sources.html)
184 | - [Public Module Registry](https://www.terraform.io/docs/registry/index.html)
185 |
--------------------------------------------------------------------------------
/challenges/06-publicmoduleregistry/README.md:
--------------------------------------------------------------------------------
1 | # 06 - Public Module Registry
2 |
3 | ## Expected Outcome
4 |
5 | In this challenge, you will take a look at the public [Module Registry](https://registry.terraform.io/) and create a Virtual Machine from verified  Public Modules.
6 |
7 | ## How to
8 |
9 | ### Navigate the Public Module Registry
10 |
11 | Open your browser and navigate to the [Module Registry](https://registry.terraform.io/).
12 |
13 | Search for "Compute" which will yield all compute resources in the registry.
14 |
15 | Now Filter By 'azurerm' which should give you (among others) the Microsoft Azure Compute Module.
16 |
17 | If you are having issues locating the module, you can find it directly at [https://registry.terraform.io/modules/Azure/compute/azurerm/1.1.7](https://registry.terraform.io/modules/Azure/compute/azurerm/1.1.7).
18 |
19 | Search again for "Networking" and apply the same Filter By, which should give you the Microsoft Azure Networking Module.
20 |
21 | If you are having issues locating the module, you can find it directly at [https://registry.terraform.io/modules/Azure/network/azurerm/2.0.0](https://registry.terraform.io/modules/Azure/network/azurerm/2.0.0).
22 |
23 | ### Create Terraform Configuration
24 |
25 | From the Cloud Shell, change directory into a folder specific to this challenge. If you created the scaffolding in Challenge 00, then then you can use the command `cd ~/AzureWorkChallenges/challenge06/`.
26 |
27 | To create an Azure Virtual Machine we need the networking in place, to do so we will be using both the modules above.
28 | The Networking module will create the Virtual Network and Subnet, then the Compute module will use that subnet as an input to create its Virtual Machine.
29 |
30 | Create a `main.tf` file in this directory and add the networking module.
31 |
32 | The following will configure the module to create a single Virtual Network and a Subnet.
33 |
34 | ```hcl
35 | module "network" {
36 | source = "Azure/network/azurerm"
37 | version = "2.0.0"
38 | resource_group_name = "myapp-networking"
39 | location = "eastus"
40 |
41 | tags = {
42 | environment = "dev"
43 | }
44 | }
45 | ```
46 |
47 | Run a `terraform init` and `terraform plan` to verify that all the resources look correct.
48 |
49 | View Output
50 |
51 |
52 | ```sh
53 | An execution plan has been generated and is shown below.
54 | Resource actions are indicated with the following symbols:
55 | + create
56 |
57 | Terraform will perform the following actions:
58 |
59 | + module.network.azurerm_resource_group.network
60 | id:
61 | location: "eastus"
62 | name: "myapp-networking"
63 | tags.%:
64 |
65 | + module.network.azurerm_subnet.subnet
66 | id:
67 | address_prefix: "10.0.1.0/24"
68 | ip_configurations.#:
69 | name: "subnet1"
70 | resource_group_name: "myapp-networking"
71 | virtual_network_name: "acctvnet"
72 |
73 | + module.network.azurerm_virtual_network.vnet
74 | id:
75 | address_space.#: "1"
76 | address_space.0: "10.0.0.0/16"
77 | location: "eastus"
78 | name: "acctvnet"
79 | resource_group_name: "myapp-networking"
80 | subnet.#:
81 | tags.%: "1"
82 | tags.environment: "dev"
83 |
84 |
85 | Plan: 3 to add, 0 to change, 0 to destroy.
86 | ```
87 |
88 |
89 |
90 |
91 | Run `terraform apply` to create the infrastructure.
92 |
93 | ### View Outputs
94 |
95 | The Public Registry contains a lot of information about the module. Navigate to the outputs tab for the [Networking Module](https://registry.terraform.io/modules/Azure/network/azurerm/2.0.0?tab=outputs).
96 |
97 | We can see the outputs we should expect and a short description of each of them.
98 |
99 | 
100 |
101 | Now that we have the networking infrastructure applied, we can view the outputs with terraform by running `terraform output -module network`.
102 |
103 | > Note: Because we are using a module, the outputs are not available at the root module, hence the need to specify the `-module network` option.
104 |
105 | ```sh
106 | $ terraform output -module network
107 | vnet_address_space = [
108 | 10.0.0.0/16
109 | ]
110 | vnet_id = /subscriptions/.../resourceGroups/myapp-networking/providers/Microsoft.Network/virtualNetworks/acctvnet
111 | vnet_location = eastus
112 | vnet_name = acctvnet
113 | vnet_subnets = [
114 | /subscriptions/.../resourceGroups/myapp-networking/providers/Microsoft.Network/virtualNetworks/acctvnet/subnets/subnet1
115 | ]
116 | ```
117 |
118 | ### Add Compute Module - Windows
119 |
120 | With Networking in place you can now add the Compute module to create a Windows Virtual Machine.
121 |
122 | ```hcl
123 | module "windowsservers" {
124 | source = "Azure/compute/azurerm"
125 | version = "1.1.7"
126 | resource_group_name = "myapp-compute-windows"
127 | location = "eastus"
128 | admin_password = "ComplxP@ssw0rd!"
129 | vm_os_simple = "WindowsServer"
130 | nb_public_ip = 0
131 | vnet_subnet_id = "${module.network.vnet_subnets[0]}"
132 | }
133 | ```
134 |
135 | Run a `terraform init` and `terraform plan` to verify that all the resources look correct.
136 |
137 | When running a plan you may run into the following error:
138 | 
139 |
140 | To get past this, simply run the `az login` command and follow the prompts.
141 |
142 | > Note: Take a minute to analyse why you needed to run another `terraform init` command before you could run a plan.
143 |
144 | View Output
145 |
146 |
147 | ```sh
148 | An execution plan has been generated and is shown below.
149 | Resource actions are indicated with the following symbols:
150 | + create
151 |
152 | Terraform will perform the following actions:
153 |
154 | + module.windowsservers.azurerm_availability_set.vm
155 | id:
156 | location: "eastus"
157 | managed: "true"
158 | name: "myvm-avset"
159 | platform_fault_domain_count: "2"
160 | platform_update_domain_count: "2"
161 | resource_group_name: "myapp-compute"
162 | tags.%:
163 |
164 | + module.windowsservers.azurerm_network_interface.vm
165 | id:
166 | applied_dns_servers.#:
167 | dns_servers.#:
168 | enable_ip_forwarding: "false"
169 | internal_dns_name_label:
170 | internal_fqdn:
171 | ip_configuration.#: "1"
172 | ip_configuration.0.load_balancer_backend_address_pools_ids.#:
173 | ip_configuration.0.load_balancer_inbound_nat_rules_ids.#:
174 | ip_configuration.0.name: "ipconfig0"
175 | ip_configuration.0.primary:
176 | ip_configuration.0.private_ip_address:
177 | ip_configuration.0.private_ip_address_allocation: "dynamic"
178 | ip_configuration.0.public_ip_address_id: "${length(azurerm_public_ip.vm.*.id) > 0 ? element(concat(azurerm_public_ip.vm.*.id, list(\"\")), count.index) : \"\"}"
179 | ip_configuration.0.subnet_id: "/subscriptions/27e9ff76-ce7b-4176-b2bb-4d3f40e1c999/resourceGroups/myapp-networking/providers/Microsoft.Network/virtualNetworks/acctvnet/subnets/subnet1"
180 | location: "eastus"
181 | mac_address:
182 | name: "nic-myvm-0"
183 | network_security_group_id: "${azurerm_network_security_group.vm.id}"
184 | private_ip_address:
185 | private_ip_addresses.#:
186 | resource_group_name: "myapp-compute"
187 | tags.%:
188 | virtual_machine_id:
189 |
190 | + module.windowsservers.azurerm_network_security_group.vm
191 | id:
192 | location: "eastus"
193 | name: "myvm-3389-nsg"
194 | resource_group_name: "myapp-compute"
195 | security_rule.#: "1"
196 | security_rule.0.access: "Allow"
197 | security_rule.0.description: "Allow remote protocol in from all locations"
198 | security_rule.0.destination_address_prefix: "*"
199 | security_rule.0.destination_port_range: "3389"
200 | security_rule.0.direction: "Inbound"
201 | security_rule.0.name: "allow_remote_3389_in_all"
202 | security_rule.0.priority: "100"
203 | security_rule.0.protocol: "tcp"
204 | security_rule.0.source_address_prefix: "*"
205 | security_rule.0.source_port_range: "*"
206 | tags.%:
207 |
208 | + module.windowsservers.azurerm_public_ip.vm
209 | id:
210 | domain_name_label: "winsimplevmips"
211 | fqdn:
212 | ip_address:
213 | location: "eastus"
214 | name: "myvm-0-publicIP"
215 | public_ip_address_allocation: "dynamic"
216 | resource_group_name: "myapp-compute"
217 | tags.%:
218 |
219 | + module.windowsservers.azurerm_resource_group.vm
220 | id:
221 | location: "eastus"
222 | name: "myapp-compute"
223 | tags.%: "1"
224 | tags.source: "terraform"
225 |
226 | + module.windowsservers.azurerm_virtual_machine.vm-windows
227 | id:
228 | availability_set_id: "${azurerm_availability_set.vm.id}"
229 | boot_diagnostics.#: "1"
230 | boot_diagnostics.0.enabled: "false"
231 | delete_data_disks_on_termination: "false"
232 | delete_os_disk_on_termination: "false"
233 | location: "eastus"
234 | name: "myvm0"
235 | network_interface_ids.#:
236 | os_profile.#: "1"
237 | os_profile.249456377.admin_password:
238 | os_profile.249456377.admin_username: "azureuser"
239 | os_profile.249456377.computer_name: "myvm0"
240 | os_profile.249456377.custom_data:
241 | os_profile_windows_config.#: "1"
242 | os_profile_windows_config.429474957.additional_unattend_config.#: "0"
243 | os_profile_windows_config.429474957.enable_automatic_upgrades: "false"
244 | os_profile_windows_config.429474957.provision_vm_agent: "false"
245 | os_profile_windows_config.429474957.winrm.#: "0"
246 | resource_group_name: "myapp-compute"
247 | storage_image_reference.#: "1"
248 | storage_image_reference.3904372903.id: ""
249 | storage_image_reference.3904372903.offer: "WindowsServer"
250 | storage_image_reference.3904372903.publisher: "MicrosoftWindowsServer"
251 | storage_image_reference.3904372903.sku: "2016-Datacenter"
252 | storage_image_reference.3904372903.version: "latest"
253 | storage_os_disk.#: "1"
254 | storage_os_disk.0.caching: "ReadWrite"
255 | storage_os_disk.0.create_option: "FromImage"
256 | storage_os_disk.0.disk_size_gb:
257 | storage_os_disk.0.managed_disk_id:
258 | storage_os_disk.0.managed_disk_type: "Premium_LRS"
259 | storage_os_disk.0.name: "osdisk-myvm-0"
260 | tags.%: "1"
261 | tags.source: "terraform"
262 | vm_size: "Standard_DS1_V2"
263 |
264 | + module.windowsservers.random_id.vm-sa
265 | id:
266 | b64:
267 | b64_std:
268 | b64_url:
269 | byte_length: "6"
270 | dec:
271 | hex:
272 | keepers.%: "1"
273 | keepers.vm_hostname: "myvm"
274 |
275 |
276 | Plan: 7 to add, 0 to change, 0 to destroy.
277 | ```
278 |
279 |
280 |
281 |
282 | Before applying, take a look at all the resources that are going to be created from our simple `module` block.
283 |
284 | Run `terraform apply` to create the infrastructure.
285 |
286 | ### Clean up
287 |
288 | Run `terraform destroy` to remove everything we created.
289 |
290 | ## Advanced areas to explore
291 |
292 | 1. Add a public ip to the Windows Compute instance using additional parameters built into the Compute module.
293 | 1. Create a second module instance of Compute to stand up a Linux Virtual Machine.
294 |
295 | ## Resources
296 |
297 | - [Azurerm Networking Moduel Source](https://github.com/Azure/terraform-azurerm-network)
298 | - [Azurerm Compute Module Source](https://github.com/Azure/terraform-azurerm-compute)
299 | - [Network Security Groups](https://docs.microsoft.com/en-us/azure/virtual-network/virtual-networks-nsg)
300 |
--------------------------------------------------------------------------------
/challenges/07-remotebackend/README.md:
--------------------------------------------------------------------------------
1 | # 07 - Remote Backend
2 |
3 | ## Expected Outcome
4 |
5 | In this challenge, you will move your state file to a remote backend.
6 |
7 | ## How to
8 |
9 | ### Create Azure Storage Account
10 |
11 | In the Portal, create a SA.
12 |
13 | Get the account information, including SAS token.
14 |
15 | Create a Blob Container
16 |
17 | ### Config Backend
18 |
19 | Update your configuration with the info:
20 |
21 | ```hcl
22 | terraform {
23 | backend {
24 | account = ""
25 | key = ""
26 | name = ""
27 | }
28 | }
29 | ```
30 |
31 | Run `terraform init`.
32 |
33 | ### View Lock State
34 |
35 | Run a plan and view the file in the portal, notice how a lease is put on it.
36 |
--------------------------------------------------------------------------------
/challenges/08-setupterraformenterprise/README.md:
--------------------------------------------------------------------------------
1 | # 08 - Setup Terraform Enterprise
2 |
3 | ## Expected Outcome
4 |
5 | In this challenge, you will create your Terraform Enterprise trial and your first workspace to build infrastructure in Azure.
6 |
7 | This challenge will require that you have a github account so that you can fork repositories. Be sure to login before beginning.
8 |
9 | ## How to
10 |
11 | ### Create Service Principal
12 |
13 | Create a Service Principal on your Azure Subscription that Terraform will use to authenticate.
14 | To do this we need to get the following:
15 |
16 | - Tenant ID
17 | - Subscription ID
18 | - Client ID
19 | - Client Secret
20 |
21 | The tenant and subscription info are static, but we need to generate that service principal to get the Client ID and Secret.
22 | To make things easy here is a one line command to get the job done:
23 |
24 | ```sh
25 | az ad sp create-for-rbac -n TerraformAzureWorkshop --role="Contributor" --scopes /subscriptions/$(az account show -o tsv --query id)
26 | ```
27 |
28 | > Note: As mentioned above, this command might not work in the cmd shell in Windows. If you can't use PowerShell or the Git bash, you should be able to separate this into multiple commands to get around cmd shell limitations, first getting your account ID and using that in the second query, like so:
29 | >
30 | > `az account show -o tsv --query id`
31 | >
32 | > `az ad sp create-for-rbac -n TerraformAzureWorkshop --role="Contributor" --scopes /subscriptions/`
33 |
34 | You may see output stating "Retrying", this is normal and is just the CLI waiting for the role to be created.
35 |
36 | When everything is complete you should see something like this:
37 |
38 | ```sh
39 | Retrying role assignment creation: 1/36
40 | Retrying role assignment creation: 2/36
41 | Retrying role assignment creation: 3/36
42 | Retrying role assignment creation: 4/36
43 | {
44 | "appId": "THIS IS YOUR CLIENT ID",
45 | "displayName": "TerraformAzureWorkshop",
46 | "name": "http://TerraformAzureWorkshop",
47 | "password": "THIS IS YOUR CLIENT PASSWORD",
48 | "tenant": "THIS IS YOUR TENANT ID"
49 | }
50 | ```
51 |
52 | > The subscription id can be seen in the Azure Portal, or by running the Azure CLI command `az account show`
53 |
54 | Take note of all 4 of these values and keep them safe, you will need to access them throughout the workshop.
55 |
56 | > NOTE: It is a good idea to remove this Service Principal after the workshop! Using the ID of the service principal, you can run an `az ad sp delete --id `. See [here](https://docs.microsoft.com/en-us/cli/azure/ad/sp?view=azure-cli-latest) for more details.
57 |
58 | ### Set Azure Credentials
59 |
60 | Terraform Enterprise uses a Service Principal to authenticate Terraform for use with Azure.
61 |
62 | Keep track of the following environment variables based on the Service Principal:
63 |
64 | ```sh
65 | export ARM_TENANT_ID=
66 | export ARM_SUBSCRIPTION_ID=
67 | export ARM_CLIENT_ID=
68 | export ARM_CLIENT_SECRET=
69 | ```
70 |
71 | ### Fork the Repository
72 |
73 | Open up a browser and navigate to the pre-built repository https://github.com/azure-terraform-workshop/azureworkshop-workspaces.
74 |
75 | In the top right, click the `Fork` button.
76 |
77 | 
78 |
79 | Follow the prompts which will fork the repository into your own space you control.
80 |
81 | > Note: We need to fork this repository to allow you to connect it to Terraform Enterprise which will create webhooks to get vital information about how and when the repository changes.
82 |
83 | You should land in your forked version of the repository.
84 |
85 | 
86 |
87 |
88 | ### azureworkshop-workspaces repository
89 |
90 | There is not much to this repository, just a couple folders that we will use in the next few challenges.
91 |
92 | **app-dev** - Simple set of infrastructure to deploy from Terraform Enterprise.
93 |
94 | **app-dev-modules** - More complex set infrastructure utilizing the Private Module Registry. Ignore this for now, we will use this in the next Challenge.
95 |
96 | ### Create a Trial Account for Terraform Enterprise
97 |
98 | Register for a [Terraform Enterprise Trial](https://app.terraform.io/account/new?trial=terraform).
99 | 
100 |
101 | If you are working on this today with others from your organization, you can create a single trial and work together through the last few challenges.
102 |
103 | ### Create a New Organization
104 |
105 | Pick a name that includes your name. Example: 'tstraubworkshop'
106 |
107 | 
108 |
109 | > __Note:__ Organization must be globally unique.
110 |
111 | Verify you can login to your Terraform Enterprise organization.
112 |
113 | Now you are ready to start using Terraform Enterprise!
114 |
115 | ### Create a New Workspace
116 |
117 | Click the "New Workspace" button.
118 |
119 | Pick a name that indicates the intent of the infrastructure. Example: 'app-dev'
120 |
121 | 
122 |
123 | ### Setup VCS
124 |
125 |
126 |
127 | You won't have any "Source" options, so click the "+" button to connect Terraform Enterprise to your source control. You will see the following screen asking you to add a VCS root. Click the "Add VCS Provider" button to continue:
128 |
129 | 
130 |
131 | You will be brought to the Add VCS Provider page:
132 |
133 | 
134 |
135 | Follow the instructions for any of the following VCS providers (we are going to be using Github):
136 |
137 | - [Github](https://www.terraform.io/docs/enterprise/vcs/github.html)
138 | - [Github Enterprise](https://www.terraform.io/docs/enterprise/vcs/github-enterprise.html)
139 | - [GitLab](https://www.terraform.io/docs/enterprise/vcs/gitlab-com.html)
140 | - [GitLab EE and CE](https://www.terraform.io/docs/enterprise/vcs/gitlab-eece.html)
141 | - [Bitbucket Cloud](https://www.terraform.io/docs/enterprise/vcs/bitbucket-cloud.html)
142 | - [Bitbucket Server](https://www.terraform.io/docs/enterprise-legacy/index.html)
143 |
144 | > Note: This only has to be done once for each Version Control Provider.
145 |
146 | > Note: You will need to update your placeholder URL to successfully connect/create your GitHub VCS root. Make sure you grab your authorization callback URL from GitHub as defined in Step 3 of the instructions for GitHub above. You may not see the menus described in Step 2.
147 |
148 | After this is done, you may have to go back and create your workspace if you didn't do so before you created the VCS provider. You can do so by navigating to the Workspaces tab at the top of the page and clicking the "New Workspace" button, choosing your GitHub VCS provider during creation.
149 |
150 | ### Connect Workspace
151 |
152 | Connect your workspace to your VCS.
153 |
154 | 
155 |
156 | Set working directory and branch properly!
157 |
158 | ### Configure Variables
159 |
160 | Now that you have a workspace, navigate to the variables.
161 |
162 | Set a the Terraform Variable "name" to something unique. Example "app-dev". Click "Save".
163 |
164 | Set Environment Variables for your Azure Service Principal (be sure check the 'sensitive' checkbox to hide these values):
165 |
166 | - ARM_TENANT_ID
167 | - ARM_SUBSCRIPTION_ID
168 | - ARM_CLIENT_ID
169 | - ARM_CLIENT_SECRET
170 |
171 | > Note: You used commands to get/set this information in Step 1 [here.](../01-connectingtoazure/README.md) Refer back to it if you need to refresh your memory.
172 |
173 | > Note: Remember also that some aliases/commands don't work as expected from a Windows cmd shell; if you have any issues with the commands you can try to run them either in the Git bash or PowerShell.
174 |
175 | Click "Save".
176 |
177 | 
178 |
179 | ### Run a Plan
180 |
181 | Click the "Queue Plan" button.
182 | 
183 |
184 | ### View the Plan
185 |
186 | 
187 |
188 | ### Run an Apply
189 |
190 | Enter a comment and then apply by clicking "Confirm & Apply".
191 |
192 | 
193 |
194 | ### View the Apply
195 |
196 | 
197 |
198 | ## Advanced areas to explore
199 |
200 | 1. Explore state versions after the apply.
201 | 1. Add another folder in the repository for 'app-prod' and create another workspace with different settings.
202 | 1. Push a change to the repository with the workspaces in it, what happens in Terraform Enterprise?
203 |
204 | ## Resources
205 |
206 | - [TFE Access](https://www.terraform.io/docs/enterprise/getting-started/access.html)
207 |
--------------------------------------------------------------------------------
/challenges/09-privatemoduleregistry/README.md:
--------------------------------------------------------------------------------
1 | # 09 - Private Module Registry with Terraform Enterprise
2 |
3 | ## Expected Outcome
4 |
5 | In this challenge you will register some modules with your Private Module Registry then reference them in a workspace.
6 |
7 | ## How to:
8 |
9 | ### Fork the Module Repositories
10 |
11 | Just like in the last challenge, you are going to fork the following repos into your own GitHub account:
12 |
13 | - https://github.com/azure-terraform-workshop/terraform-azurerm-networking.git
14 | - https://github.com/azure-terraform-workshop/terraform-azurerm-webserver.git
15 | - https://github.com/azure-terraform-workshop/terraform-azurerm-appserver.git
16 | - https://github.com/azure-terraform-workshop/terraform-azurerm-dataserver.git
17 |
18 | Each of these repositories represents a module that can be developed and versioned independently.
19 |
20 | ### Add Modules
21 |
22 | Navigate back to Terraform Enterprise and click the "Modules" menu at the top of the page. From there click the "+ Add Module" button.
23 |
24 | 
25 |
26 | You are now ready to add your modules.
27 |
28 | 
29 |
30 | Enter the name of the source repository you forked in the previous step. For example: 'YOUR_GITHUB_USERNAME/terraform-azurerm-networking`.
31 |
32 | Click "Publish Module".
33 |
34 | This will query the repository for necessary files and tags used for versioning.
35 |
36 | Congrats, you are done!
37 |
38 | Ok, not really...
39 |
40 | Repeat this step for the other three modules:
41 |
42 | - terraform-azurerm-appserver
43 | - terraform-azurerm-dataserver
44 | - terraform-azurerm-webserver
45 |
46 | ### Consume Modules
47 |
48 | Create a new workspace just like in the previous Challenge, except this time enter the working directory of "app-dev-modules" that will reference your the modules you just added.
49 |
50 | 
51 |
52 | ### Configure Variables
53 |
54 | Set the Terraform Variables:
55 |
56 | - 'name' - A unique environment name such as `devmodules`
57 | - 'location' - An Azure region such as `eastus` or `centralus`
58 | - 'username' (sensitive) - A username for the VM's
59 | > Note: this can not be "admin"
60 | - 'password' (sensitive) - A password for the VM's
61 | > NOTE: password must be between 6-72 characters long and must satisfy at least 3 of password complexity requirements from the following:
62 | > 1. Contains an uppercase character
63 | > 2. Contains a lowercase character
64 | > 3. Contains a numeric digit
65 | > 4. Contains a special character
66 | - 'vnet_address_spacing' (HCL) - The Vnet Address space
67 | ```hcl
68 | ["10.0.0.0/16"]
69 | ```
70 | - 'subnet_address_prefixes' (HCL) - The Subnet Address spaces representing 3 subnets
71 | ```hcl
72 | [
73 | "10.0.0.0/24",
74 | "10.0.1.0/24",
75 | "10.0.2.0/24",
76 | ]
77 | ```
78 |
79 | Set Environment Variables for your Azure Service Principal (be sure check the 'sensitive' checkbox to hide these values):
80 |
81 | - ARM_TENANT_ID
82 | - ARM_SUBSCRIPTION_ID
83 | - ARM_CLIENT_ID
84 | - ARM_CLIENT_SECRET
85 |
86 | ### Run a Plan
87 |
88 | Click the "Queue Plan" button.
89 |
90 | 
91 |
92 | Wait for the Plan to complete.
93 |
94 | ### Fix the Errors
95 |
96 | The `/app-dev-modules/main.tf` file references the wrong modules source. Update all the modules sources (in your forked `azureworkshop-workspaces` GitHub repo) to match your Terraform Enterprise Organization.
97 |
98 | For example:
99 |
100 | Change this:
101 |
102 | ```hcl
103 | module "networking" {
104 | source = "app.terraform.io/cardinalsolutions/networking/azurerm"
105 | version = "0.0.1"
106 | ...
107 | }
108 | ```
109 |
110 | To something like this:
111 | ```hcl
112 | module "networking" {
113 | source = "app.terraform.io/YOUR_TFE_ORGANIZATION/networking/azurerm"
114 | version = "0.0.1"
115 | ...
116 | }
117 | ```
118 |
119 | Queue a new Plan.
120 |
121 | ### Apply the Plan
122 |
123 | Approve the plan and apply it.
124 |
125 | Watch the apply progress and complete.
126 |
127 | Login to the at Azure Portal to see your infrastructure.
128 |
129 | ### Update a Module
130 |
131 | In the `azureworkshop-workspaces` repository, navigate to the `app-dev-modules/main.tf` file and update one (or several) of the modules versions from "0.0.1" to "0.0.2".
132 |
133 | Commit your change and see what the changes show in the plan. What was the difference between version 0.0.1 and 0.0.2? Does this look like a safe change to make?
134 |
135 | ## Advanced areas to explore
136 |
137 | 1. Add another workspace using a different combination of the 3-tier application.
138 | 1. Make a change to one of the modules and commit that to the repository. How do you get a new version to show in your Private Module Registry?
139 |
140 | ## Resources
141 |
142 | - [Private Registries](https://www.terraform.io/docs/registry/private.html)
143 | - [Publishing Modules](https://www.terraform.io/docs/registry/modules/publish.html)
--------------------------------------------------------------------------------
/challenges/10-sentinelpolicy/README.md:
--------------------------------------------------------------------------------
1 | # 10 - Sentinel Policy with Terraform Enterprise
2 |
3 | ## Expected Outcome
4 |
5 | In this challenge, you will see how you can apply policies around your Azure subscriptions using Sentinel Policies.
6 |
7 |
8 | ## How to
9 |
10 | ### View Policies
11 |
12 | In the Terraform Enterprise web app, click on your organization -> Organization Settings
13 |
14 | https://app.terraform.io/app/YOUR_TFE_ORGANIZATION/settings/policies
15 |
16 | 
17 |
18 | ### Create Policy in App
19 |
20 | Click "Create new Policy"
21 |
22 | 
23 |
24 | ### Create Policy from API
25 |
26 | Create the following policy:
27 |
28 | __Policy Name:__ ResourceGroupRequireTag
29 |
30 | __Policy Enforcement:__ advisory (logging only)
31 |
32 | __Policy Code:__
33 |
34 | ```hcl
35 | import "tfplan"
36 |
37 | required_tags = [
38 | "owner",
39 | "environment",
40 | ]
41 |
42 | getTags = func(group) {
43 | tags = keys(group.applied.tags)
44 |
45 | for required_tags as t {
46 | if t not in tags {
47 | print("Resource Missing Tag:", t)
48 | return false
49 | }
50 | }
51 |
52 | return true
53 | }
54 | main = rule {
55 | all tfplan.resources.azurerm_resource_group as _, groups {
56 | all groups as _, group {
57 | getTags(group)
58 | }
59 | }
60 | }
61 | ```
62 |
63 | ### Run a Plan
64 |
65 | Queue a plan for the workspace `app-dev`.
66 |
67 | ### Review the Plan
68 |
69 | Will see the plan was successful but there was a policy failure, however the option to Apply is still available.
70 |
71 | ### Update the Policy
72 |
73 | Update the Policy Enforcement to be `hard-mandatory`.
74 |
75 | ### Run a Plan
76 |
77 | Queue a plan for the workspace `app-dev`.
78 |
79 | ### Review the Plan
80 |
81 | This time the the run fails due to the hard enforcement.
82 |
83 | ### Update Workspace
84 |
85 | In the `app-dev` workspace, add the following to the `azurerm_resource_group` declaration:
86 |
87 | ```hcl
88 | resource "azurerm_resource_group" "module" {
89 |
90 | ...
91 |
92 | tags {
93 | Owner = "me"
94 | }
95 | }
96 | ```
97 |
98 | Save and commit the code to your repository.
99 |
100 | ### Run a Plan
101 |
102 | Run another plan.
103 |
104 | > Note: You may need to discard the last non-applied build.
105 |
106 | ### Review the Plan
107 |
108 | The plan should succeed and now pass the sentinel policy check.
109 |
110 | ## Advanced areas to explore
111 |
112 | 1. Write another Sentinel Policy restricting VM types in Azure.
113 |
114 | ## Resources
115 |
116 | - [Policy](https://app.terraform.io/app/cardinalsolutions/settings/policies)
117 | - [Sentinel Language Spec](https://docs.hashicorp.com/sentinel/language/spec)
118 |
--------------------------------------------------------------------------------
/docs/AzureTerraformWorkshopPresentation.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/docs/AzureTerraformWorkshopPresentation.pptx
--------------------------------------------------------------------------------
/event-invitation.md:
--------------------------------------------------------------------------------
1 | # INVITE: Infrastructure as Code - Building Azure infrastructure using Terraform and Terraform Enterprise
2 |
3 | Infrastructure as Code allows the management of your infrastructure including networks, virtual machines, platform services, and load balancers. Utilizing Terraform configuration to describe the desired Azure resources needed for an environment ensures that consistent infrastructure is created each time it is deployed.
4 |
5 | This Workshop is focused on:
6 |
7 | - Understanding Infrastructure as Code concepts
8 | - Terraform as an Infrastructure as Code provider
9 | - Terraform Enterprise features and benefits
10 |
11 | ### Understanding Infrastructure as Code concepts
12 |
13 | Infrastructure as Code allows DevOps teams to deploy production-like environments early in the development cycle. Declarative code representing infrastructure can also be validated and approved before moving downstream. Using Azure's cloud platform, dynamic creation, modification, and deletion of infrastructure that is built rapidly, reliably, and at scale.
14 |
15 | ### Terraform as an Infrastructure as Code provider
16 |
17 | Utilizing a simple configuration language Terraform allows for a human readable representation of your infrastructure. The execution plan phase allows infrastructure to be viewed and approved with full confidence that you are changing your environment how you intended.
18 |
19 | ### Terraform Enterprise features and benefits
20 |
21 | Terraform Enterprise offers a single location to perform all of your Terraform workflows. It is a central place to manage your infrastructure and includes collaboration, governance, and private module registry capabilities.
22 |
23 | ## Target Audience
24 |
25 | The event is targeted at DevOps teams and managers looking to automate infrastructure deployments. The session will start with core concepts around IaC and then deep dive into how Terraform with hand-on coding.
26 |
27 | ## Gives and Gets
28 |
29 | ### Gets
30 |
31 | - Learn alongside Cardinal and Microsoft Engineers to help you understand the design considerations to accelerate your Azure infrastructure deployments.
32 | - Engage in hands on coding to help understand the options available to designing your IaC solution.
33 | - Post-Hackfest engagements to ensure your continued success.
34 |
35 | ### Gives
36 |
37 | - Attend the Workshop with your leaders in DevOps and IT Pro’s. We strongly recommend including a technical decision maker and architect level resource.
38 | - Defined Azure Subscription to deploy real infrastructure to during the event.
39 | - Initial set of infrastructure you are looking to create as an environment.
40 |
--------------------------------------------------------------------------------
/examples/simple-rg/main.tf:
--------------------------------------------------------------------------------
1 | resource "azurerm_resource_group" "test" {
2 | name = "test-rg-tom"
3 | location = "centralus"
4 | }
--------------------------------------------------------------------------------
/examples/ssh/main.tf:
--------------------------------------------------------------------------------
1 | locals{
2 | ssh_key_name = "bastion"
3 | }
4 | resource "tls_private_key" "main" {
5 | algorithm = "RSA"
6 | }
7 |
8 | resource "local_file" "private" {
9 | content = "${tls_private_key.main.private_key_pem}"
10 | filename = "./id_rsa_${local.ssh_key_name}.pem"
11 |
12 | provisioner "local-exec" {
13 | command = "chmod 600 ./id_rsa_${local.ssh_key_name}.pem"
14 | }
15 | }
16 |
17 | resource "local_file" "public" {
18 | content = "${tls_private_key.main.public_key_openssh}"
19 | filename = "./id_rsa_${local.ssh_key_name}.pub"
20 |
21 | provisioner "local-exec" {
22 | command = "chmod 600 ./id_rsa_${local.ssh_key_name}.pub"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/img/2018-04-07-15-08-41.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-07-15-08-41.png
--------------------------------------------------------------------------------
/img/2018-04-07-16-54-28.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-07-16-54-28.png
--------------------------------------------------------------------------------
/img/2018-04-14-12-58-33.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-14-12-58-33.png
--------------------------------------------------------------------------------
/img/2018-04-14-12-59-54.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-14-12-59-54.png
--------------------------------------------------------------------------------
/img/2018-04-14-13-10-52.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-14-13-10-52.png
--------------------------------------------------------------------------------
/img/2018-04-14-13-21-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-14-13-21-32.png
--------------------------------------------------------------------------------
/img/2018-04-14-14-04-56.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-14-14-04-56.png
--------------------------------------------------------------------------------
/img/2018-04-14-14-10-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-14-14-10-32.png
--------------------------------------------------------------------------------
/img/2018-04-14-14-11-20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-14-14-11-20.png
--------------------------------------------------------------------------------
/img/2018-04-14-14-12-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-14-14-12-05.png
--------------------------------------------------------------------------------
/img/2018-04-14-14-13-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-14-14-13-01.png
--------------------------------------------------------------------------------
/img/2018-04-14-14-13-52.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-14-14-13-52.png
--------------------------------------------------------------------------------
/img/2018-04-14-14-15-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-14-14-15-02.png
--------------------------------------------------------------------------------
/img/2018-04-15-13-09-55.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-15-13-09-55.png
--------------------------------------------------------------------------------
/img/2018-04-15-19-23-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-15-19-23-40.png
--------------------------------------------------------------------------------
/img/2018-04-16-20-02-58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-16-20-02-58.png
--------------------------------------------------------------------------------
/img/2018-04-16-20-03-30.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-04-16-20-03-30.png
--------------------------------------------------------------------------------
/img/2018-05-07-18-08-30.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-07-18-08-30.png
--------------------------------------------------------------------------------
/img/2018-05-07-18-11-33.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-07-18-11-33.png
--------------------------------------------------------------------------------
/img/2018-05-07-18-13-28.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-07-18-13-28.png
--------------------------------------------------------------------------------
/img/2018-05-07-18-20-56.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-07-18-20-56.png
--------------------------------------------------------------------------------
/img/2018-05-07-18-29-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-07-18-29-10.png
--------------------------------------------------------------------------------
/img/2018-05-09-09-10-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-09-09-10-24.png
--------------------------------------------------------------------------------
/img/2018-05-09-10-20-28.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-09-10-20-28.png
--------------------------------------------------------------------------------
/img/2018-05-09-14-55-42.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-09-14-55-42.png
--------------------------------------------------------------------------------
/img/2018-05-10-17-14-51.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-10-17-14-51.png
--------------------------------------------------------------------------------
/img/2018-05-10-17-17-27.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-10-17-17-27.png
--------------------------------------------------------------------------------
/img/2018-05-10-17-37-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-10-17-37-05.png
--------------------------------------------------------------------------------
/img/2018-05-10-17-40-35.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-10-17-40-35.png
--------------------------------------------------------------------------------
/img/2018-05-11-11-22-22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-11-11-22-22.png
--------------------------------------------------------------------------------
/img/2018-05-11-11-26-22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-11-11-26-22.png
--------------------------------------------------------------------------------
/img/2018-05-14-07-27-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-14-07-27-11.png
--------------------------------------------------------------------------------
/img/2018-05-14-08-18-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-14-08-18-48.png
--------------------------------------------------------------------------------
/img/2018-05-28-12-25-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-28-12-25-01.png
--------------------------------------------------------------------------------
/img/2018-05-28-12-27-31.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-28-12-27-31.png
--------------------------------------------------------------------------------
/img/2018-05-28-12-29-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-28-12-29-06.png
--------------------------------------------------------------------------------
/img/2018-05-28-12-30-33.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-28-12-30-33.png
--------------------------------------------------------------------------------
/img/2018-05-28-13-58-49.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-28-13-58-49.png
--------------------------------------------------------------------------------
/img/2018-05-28-14-01-30.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-28-14-01-30.png
--------------------------------------------------------------------------------
/img/2018-05-28-14-03-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-28-14-03-05.png
--------------------------------------------------------------------------------
/img/2018-05-28-14-04-39.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-28-14-04-39.png
--------------------------------------------------------------------------------
/img/2018-05-28-14-05-39.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-28-14-05-39.png
--------------------------------------------------------------------------------
/img/2018-05-28-14-09-39.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-05-28-14-09-39.png
--------------------------------------------------------------------------------
/img/2018-06-07-16-23-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-06-07-16-23-29.png
--------------------------------------------------------------------------------
/img/2018-11-02-12-59-21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2018-11-02-12-59-21.png
--------------------------------------------------------------------------------
/img/2019-05-08-09-24-12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2019-05-08-09-24-12.png
--------------------------------------------------------------------------------
/img/2019-05-08-09-27-19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CardinalNow/TerraformWorkshop/24a729e333b53687951c14f31528fb330f648e0c/img/2019-05-08-09-27-19.png
--------------------------------------------------------------------------------
/solutions/01-connectingtoazure/main.tf:
--------------------------------------------------------------------------------
1 | resource "azurerm_resource_group" "main" {
2 | name = "challenge01-rg"
3 | location = "eastus"
4 | }
5 |
6 | resource "azurerm_resource_group" "count" {
7 | name = "challenge01-rg-${count.index}"
8 | location = "eastus"
9 | count = 2
10 | }
11 |
12 | // Import these resources that were manually created in the Azure Portal
13 | resource "azurerm_resource_group" "import" {
14 | name = "myportal-rg"
15 | location = "eastus"
16 |
17 | tags {
18 | terraform = "true"
19 | }
20 | }
21 |
22 | resource "azurerm_storage_account" "import" {
23 | name = "myusernamestorageaccount"
24 | resource_group_name = "${azurerm_resource_group.import.name}"
25 | location = "eastus"
26 | account_kind = "StorageV2"
27 | account_tier = "Standard"
28 | account_replication_type = "LRS"
29 | enable_https_traffic_only = true
30 |
31 | tags {
32 | terraform = "true"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/solutions/02-aci-helloworld/main.tf:
--------------------------------------------------------------------------------
1 | resource "random_pet" "main" {
2 | length = 2
3 | separator = ""
4 | }
5 |
6 | resource "azurerm_resource_group" "main" {
7 | name = "aci-helloworld"
8 | location = "eastus"
9 | }
10 |
11 | resource "azurerm_storage_account" "main" {
12 | name = "acidev${random_pet.main.id}"
13 | resource_group_name = "${azurerm_resource_group.main.name}"
14 | location = "${azurerm_resource_group.main.location}"
15 | account_tier = "Standard"
16 | account_replication_type = "LRS"
17 | }
18 |
19 | resource "azurerm_storage_share" "main" {
20 | name = "aci-test-share"
21 | resource_group_name = "${azurerm_resource_group.main.name}"
22 | storage_account_name = "${azurerm_storage_account.main.name}"
23 | quota = 1
24 | }
25 |
26 | resource "azurerm_container_group" "main" {
27 | name = "aci-helloworld"
28 | location = "${azurerm_resource_group.main.location}"
29 | resource_group_name = "${azurerm_resource_group.main.name}"
30 | ip_address_type = "public"
31 | dns_name_label = "aci-${random_pet.main.id}"
32 | os_type = "linux"
33 |
34 | container {
35 | name = "helloworld"
36 | image = "microsoft/aci-helloworld"
37 | cpu = "0.5"
38 | memory = "1.5"
39 |
40 | ports {
41 | port = 80
42 | protocol = "TCP"
43 | }
44 |
45 | environment_variables {
46 | "NODE_ENV" = "testing"
47 | }
48 |
49 | volume {
50 | name = "logs"
51 | mount_path = "/aci/logs"
52 | read_only = false
53 | share_name = "${azurerm_storage_share.main.name}"
54 |
55 | storage_account_name = "${azurerm_storage_account.main.name}"
56 | storage_account_key = "${azurerm_storage_account.main.primary_access_key}"
57 | }
58 | }
59 |
60 | container {
61 | name = "sidecar"
62 | image = "microsoft/aci-tutorial-sidecar"
63 | cpu = "0.5"
64 | memory = "1.5"
65 | }
66 |
67 | tags {
68 | environment = "testing"
69 | }
70 | }
71 |
72 | resource "azurerm_container_group" "windows" {
73 | name = "aci-iis"
74 | location = "${azurerm_resource_group.main.location}"
75 | resource_group_name = "${azurerm_resource_group.main.name}"
76 | ip_address_type = "public"
77 | dns_name_label = "aci-iis-${random_pet.main.id}"
78 | os_type = "windows"
79 |
80 | container {
81 | name = "dotnetsample"
82 | image = "microsoft/iis"
83 | cpu = "0.5"
84 | memory = "1.5"
85 |
86 | ports {
87 | port = 80
88 | protocol = "TCP"
89 | }
90 | }
91 |
92 | tags {
93 | environment = "testing"
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/solutions/03-azurevm/main.tf:
--------------------------------------------------------------------------------
1 | variable "name" {
2 | default = "challenge03"
3 | }
4 |
5 | variable "location" {
6 | default = "eastus"
7 | }
8 |
9 | # Basic Resources
10 | resource "azurerm_resource_group" "main" {
11 | name = "${var.name}-rg"
12 | location = "${var.location}"
13 | }
14 |
15 | resource "azurerm_virtual_network" "main" {
16 | name = "${var.name}-vnet"
17 | address_space = ["10.0.0.0/16"]
18 | location = "${azurerm_resource_group.main.location}"
19 | resource_group_name = "${azurerm_resource_group.main.name}"
20 | }
21 |
22 | resource "azurerm_subnet" "main" {
23 | name = "${var.name}-subnet"
24 | resource_group_name = "${azurerm_resource_group.main.name}"
25 | virtual_network_name = "${azurerm_virtual_network.main.name}"
26 | address_prefix = "10.0.1.0/24"
27 | }
28 |
29 | # VM Resources
30 | resource "azurerm_public_ip" "main" {
31 | name = "${var.name}-pubip"
32 | location = "${azurerm_resource_group.main.location}"
33 | resource_group_name = "${azurerm_resource_group.main.name}"
34 | allocation_method = "Static"
35 | }
36 |
37 | resource "azurerm_network_interface" "main" {
38 | name = "${var.name}-nic"
39 | location = "${azurerm_resource_group.main.location}"
40 | resource_group_name = "${azurerm_resource_group.main.name}"
41 |
42 | ip_configuration {
43 | name = "config1"
44 | subnet_id = "${azurerm_subnet.main.id}"
45 | public_ip_address_id = "${azurerm_public_ip.main.id}"
46 | private_ip_address_allocation = "dynamic"
47 | }
48 | }
49 |
50 | resource "azurerm_virtual_machine" "main" {
51 | name = "${var.name}-vm"
52 | location = "${azurerm_resource_group.main.location}"
53 | resource_group_name = "${azurerm_resource_group.main.name}"
54 | network_interface_ids = ["${azurerm_network_interface.main.id}"]
55 | vm_size = "Standard_A2_v2"
56 |
57 | storage_image_reference {
58 | publisher = "MicrosoftWindowsServer"
59 | offer = "WindowsServer"
60 | sku = "2016-Datacenter"
61 | version = "latest"
62 | }
63 |
64 | storage_os_disk {
65 | name = "${var.name}vm-osdisk"
66 | caching = "ReadWrite"
67 | create_option = "FromImage"
68 | managed_disk_type = "Standard_LRS"
69 | }
70 |
71 | os_profile {
72 | computer_name = "${var.name}vm"
73 | admin_username = "testadmin"
74 | admin_password = "Password1234!"
75 | }
76 |
77 | os_profile_windows_config {}
78 | }
79 |
80 | ## Outputs
81 | output "private-ip" {
82 | value = "${azurerm_network_interface.main.private_ip_address}"
83 | description = "Private IP Address"
84 | }
85 |
86 | output "public-ip" {
87 | value = "${azurerm_public_ip.main.ip_address}"
88 | description = "Public IP Address"
89 | }
90 |
--------------------------------------------------------------------------------
/solutions/04-terraformcount/main.tf:
--------------------------------------------------------------------------------
1 | variable "name" {
2 | default = "challenge04"
3 | }
4 |
5 | variable "location" {
6 | default = "eastus"
7 | }
8 |
9 | variable "vmcount" {
10 | default = 2
11 | }
12 |
13 | # Basic Resources
14 | resource "azurerm_resource_group" "main" {
15 | name = "${var.name}-rg"
16 | location = "${var.location}"
17 | }
18 |
19 | resource "azurerm_virtual_network" "main" {
20 | name = "${var.name}-vnet"
21 | address_space = ["10.0.0.0/16"]
22 | location = "${azurerm_resource_group.main.location}"
23 | resource_group_name = "${azurerm_resource_group.main.name}"
24 | }
25 |
26 | resource "azurerm_subnet" "main" {
27 | name = "${var.name}-subnet"
28 | resource_group_name = "${azurerm_resource_group.main.name}"
29 | virtual_network_name = "${azurerm_virtual_network.main.name}"
30 | address_prefix = "10.0.1.0/24"
31 | }
32 |
33 | # VM Resources
34 | resource "azurerm_public_ip" "main" {
35 | name = "${var.name}-pubip${count.index}"
36 | location = "${azurerm_resource_group.main.location}"
37 | resource_group_name = "${azurerm_resource_group.main.name}"
38 | allocation_method = "Static"
39 | count = "${var.vmcount}"
40 | }
41 |
42 | resource "azurerm_network_interface" "main" {
43 | name = "${var.name}-nic${count.index}"
44 | location = "${azurerm_resource_group.main.location}"
45 | resource_group_name = "${azurerm_resource_group.main.name}"
46 | count = "${var.vmcount}"
47 |
48 | ip_configuration {
49 | name = "config1"
50 | subnet_id = "${azurerm_subnet.main.id}"
51 |
52 | // public_ip_address_id = "${azurerm_public_ip.main.id}"
53 | public_ip_address_id = "${element(azurerm_public_ip.main.*.id, count.index)}"
54 | private_ip_address_allocation = "dynamic"
55 | }
56 | }
57 |
58 | resource "azurerm_virtual_machine" "main" {
59 | name = "${var.name}-vm${count.index}"
60 | location = "${azurerm_resource_group.main.location}"
61 | resource_group_name = "${azurerm_resource_group.main.name}"
62 |
63 | // network_interface_ids = ["${azurerm_network_interface.main.id}"]
64 | network_interface_ids = ["${element(azurerm_network_interface.main.*.id, count.index)}"]
65 | vm_size = "Standard_A2_v2"
66 | count = "${var.vmcount}"
67 |
68 | storage_image_reference {
69 | publisher = "MicrosoftWindowsServer"
70 | offer = "WindowsServer"
71 | sku = "2016-Datacenter"
72 | version = "latest"
73 | }
74 |
75 | storage_os_disk {
76 | name = "${var.name}vm${count.index}-osdisk"
77 | caching = "ReadWrite"
78 | create_option = "FromImage"
79 | managed_disk_type = "Standard_LRS"
80 | }
81 |
82 | os_profile {
83 | computer_name = "${var.name}vm${count.index}"
84 | admin_username = "testadmin"
85 | admin_password = "Password1234!"
86 | }
87 |
88 | os_profile_windows_config {}
89 | }
90 |
91 | ## Outputs
92 | output "private-ip" {
93 | value = "${azurerm_network_interface.main.*.private_ip_address}"
94 | description = "Private IP Address"
95 | }
96 |
97 | output "public-ip" {
98 | value = "${azurerm_public_ip.main.*.ip_address}"
99 | description = "Public IP Address"
100 | }
101 |
--------------------------------------------------------------------------------
/solutions/05-terraformmodules/environments/dev/main.tf:
--------------------------------------------------------------------------------
1 | variable "username" {}
2 | variable "password" {}
3 |
4 | module "myawesomewindowsvm" {
5 | source = "../../modules/my_virtual_machine"
6 | name = "awesomeapp"
7 | vm_size = "Standard_A2_v2"
8 | username = "${var.username}"
9 | password = "${var.password}"
10 | vmcount = 2
11 | }
12 |
13 | module "differentwindowsvm" {
14 | source = "../../modules/my_virtual_machine"
15 | name = "differentapp"
16 | vm_size = "Standard_A2_v2"
17 | username = "${var.username}"
18 | password = "${var.password}"
19 | }
20 |
--------------------------------------------------------------------------------
/solutions/05-terraformmodules/modules/my_virtual_machine/main.tf:
--------------------------------------------------------------------------------
1 | variable "vm_size" {}
2 | variable "username" {}
3 | variable "password" {}
4 |
5 | variable "name" {
6 | default = "challenge05"
7 | }
8 |
9 | variable "location" {
10 | default = "eastus"
11 | }
12 |
13 | variable "vmcount" {
14 | default = 2
15 | }
16 |
17 | # Basic Resources
18 | resource "azurerm_resource_group" "main" {
19 | name = "${var.name}-rg"
20 | location = "${var.location}"
21 | }
22 |
23 | resource "azurerm_virtual_network" "main" {
24 | name = "${var.name}-vnet"
25 | address_space = ["10.0.0.0/16"]
26 | location = "${azurerm_resource_group.main.location}"
27 | resource_group_name = "${azurerm_resource_group.main.name}"
28 | }
29 |
30 | resource "azurerm_subnet" "main" {
31 | name = "${var.name}-subnet"
32 | resource_group_name = "${azurerm_resource_group.main.name}"
33 | virtual_network_name = "${azurerm_virtual_network.main.name}"
34 | address_prefix = "10.0.1.0/24"
35 | }
36 |
37 | # VM Resources
38 | resource "azurerm_public_ip" "main" {
39 | name = "${var.name}-pubip${count.index}"
40 | location = "${azurerm_resource_group.main.location}"
41 | resource_group_name = "${azurerm_resource_group.main.name}"
42 | allocation_method = "Static"
43 | count = "${var.vmcount}"
44 | }
45 |
46 | resource "azurerm_network_interface" "main" {
47 | name = "${var.name}-nic${count.index}"
48 | location = "${azurerm_resource_group.main.location}"
49 | resource_group_name = "${azurerm_resource_group.main.name}"
50 | count = "${var.vmcount}"
51 |
52 | ip_configuration {
53 | name = "config1"
54 | subnet_id = "${azurerm_subnet.main.id}"
55 | public_ip_address_id = "${element(azurerm_public_ip.main.*.id, count.index)}"
56 | private_ip_address_allocation = "dynamic"
57 | }
58 | }
59 |
60 | resource "azurerm_virtual_machine" "main" {
61 | name = "${var.name}-vm${count.index}"
62 | location = "${azurerm_resource_group.main.location}"
63 | resource_group_name = "${azurerm_resource_group.main.name}"
64 | network_interface_ids = ["${element(azurerm_network_interface.main.*.id, count.index)}"]
65 | vm_size = "${var.vm_size}"
66 | count = "${var.vmcount}"
67 |
68 | storage_image_reference {
69 | publisher = "MicrosoftWindowsServer"
70 | offer = "WindowsServer"
71 | sku = "2016-Datacenter"
72 | version = "latest"
73 | }
74 |
75 | storage_os_disk {
76 | name = "${var.name}vm${count.index}-osdisk"
77 | caching = "ReadWrite"
78 | create_option = "FromImage"
79 | managed_disk_type = "Standard_LRS"
80 | }
81 |
82 | os_profile {
83 | computer_name = "${var.name}vm${count.index}"
84 | admin_username = "${var.username}"
85 | admin_password = "${var.password}"
86 | }
87 |
88 | os_profile_windows_config {}
89 | }
90 |
91 | ## Outputs
92 | output "private-ip" {
93 | value = "${azurerm_network_interface.main.*.private_ip_address}"
94 | description = "Private IP Address"
95 | }
96 |
97 | output "public-ip" {
98 | value = "${azurerm_public_ip.main.*.ip_address}"
99 | description = "Public IP Address"
100 | }
101 |
--------------------------------------------------------------------------------
/solutions/06-publicmoduleregistry/main.tf:
--------------------------------------------------------------------------------
1 | module "network" {
2 | source = "Azure/network/azurerm"
3 | version = "2.0.0"
4 | resource_group_name = "myapp-networking"
5 | location = "eastus"
6 |
7 | tags = {
8 | environment = "dev"
9 | }
10 | }
11 |
12 | module "windowsservers" {
13 | source = "Azure/compute/azurerm"
14 | version = "1.1.7"
15 | resource_group_name = "myapp-compute-windows"
16 | location = "eastus"
17 | admin_password = "ComplxP@ssw0rd!"
18 | vm_os_simple = "WindowsServer"
19 | nb_public_ip = 0
20 | vnet_subnet_id = "${module.network.vnet_subnets[0]}"
21 | }
22 |
23 | module "linuxservers" {
24 | source = "Azure/compute/azurerm"
25 | version = "1.1.7"
26 | resource_group_name = "myapp-compute-linux"
27 | location = "eastus"
28 | vm_os_simple = "UbuntuServer"
29 | nb_public_ip = 0
30 | vnet_subnet_id = "${module.network.vnet_subnets[0]}"
31 | }
32 |
--------------------------------------------------------------------------------