├── 2-terraform-variables ├── README.md ├── 3-terraform-local-variables.md ├── 2-terraform-tfvars.md └── 1-terraform-variables.md ├── 6-terraform-modules ├── terraform │ ├── modules │ │ └── acr │ │ │ ├── output.tf │ │ │ ├── main.tf │ │ │ └── variables.tf │ ├── main.tf │ └── providers.tf └── README.md ├── 5-secret-management-azure ├── terraform │ ├── variables.tf │ ├── providers.tf │ └── main.tf └── README.md ├── 1-terraform-basics ├── 3-terraform-resources │ ├── README.md │ └── 1-terraform-resources.md ├── 2-terraform-commands │ ├── README.md │ └── 1-terraform-commands.md ├── 4-terraform-azure-provider │ ├── README.md │ └── 1-azure-provider.md └── 1-terraform-installation │ ├── README.md │ ├── 1-terraform-install.md │ └── 2-vscode-install.md ├── 4-terraform-advanced ├── 1-depends-on │ ├── terraform │ │ ├── variables.tf │ │ ├── providers.tf │ │ └── main.tf │ └── README.md ├── 3-count │ ├── terraform │ │ ├── variables.tf │ │ ├── main.tf │ │ └── providers.tf │ └── README.md ├── 2-for-each │ ├── terraform │ │ ├── variables.tf │ │ ├── main.tf │ │ └── providers.tf │ └── README.md ├── 5-dynamic-blocks │ ├── terraform │ │ ├── variables.tf │ │ ├── main.tf │ │ └── providers.tf │ └── README.md ├── 4-conditional-expressions │ ├── conditional-expressions-example │ │ ├── variables.tf │ │ ├── providers.tf │ │ └── main.tf │ └── README.md └── README.md ├── 3-terraform-state ├── images │ └── terraform-plan.png ├── local-state-example │ ├── main.tf │ └── providers.tf ├── remote-state-example │ ├── main.tf │ └── providers.tf ├── scripts │ ├── 2-delete-terraform-storage.sh │ └── 1-create-terraform-storage.sh ├── README.md ├── 1-terraform-state-local-vs-remote.md ├── 3-terraform-remote-state-deploy.md └── 2-terraform-local-state-deploy.md ├── renovate.json ├── 7-terraform-azapi ├── terraform │ ├── main.tf │ ├── variables.tf │ └── providers.tf └── README.md ├── README.md └── .github └── copilot-instructions.md /2-terraform-variables/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Variables 2 | 3 | A guide on how to use variables in Terraform. 4 | 5 | -------------------------------------------------------------------------------- /6-terraform-modules/terraform/modules/acr/output.tf: -------------------------------------------------------------------------------- 1 | output "acr_id" { 2 | value = azurerm_container_registry.acr.id 3 | } -------------------------------------------------------------------------------- /5-secret-management-azure/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" { 2 | type = string 3 | default = "tamopsrg" 4 | } -------------------------------------------------------------------------------- /1-terraform-basics/3-terraform-resources/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Resources 2 | 3 | Looking at terraform resources and data references. 4 | 5 | -------------------------------------------------------------------------------- /4-terraform-advanced/1-depends-on/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" { 2 | type = string 3 | default = "tamopsrg" 4 | } -------------------------------------------------------------------------------- /3-terraform-state/images/terraform-plan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thomast1906/terraform-on-azure/HEAD/3-terraform-state/images/terraform-plan.png -------------------------------------------------------------------------------- /3-terraform-state/local-state-example/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "rg" { 2 | name = "tamopslocalrg" 3 | location = "uksouth" 4 | } -------------------------------------------------------------------------------- /3-terraform-state/remote-state-example/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "rg" { 2 | name = "tamopslocalrg" 3 | location = "uksouth" 4 | } -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /4-terraform-advanced/3-count/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_names" { 2 | type = list(string) 3 | default = ["tamopsrg", "tamopsrg2"] 4 | } -------------------------------------------------------------------------------- /4-terraform-advanced/2-for-each/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_names" { 2 | type = list(string) 3 | default = ["tamopsrg", "tamopsrg2"] 4 | } -------------------------------------------------------------------------------- /4-terraform-advanced/5-dynamic-blocks/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_names" { 2 | type = list(string) 3 | default = ["tamopsrg", "tamopsrg2"] 4 | } -------------------------------------------------------------------------------- /1-terraform-basics/2-terraform-commands/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Commands 2 | 3 | An introduction to the Terraform commands available, this will cover the basic commands to get you started with Terraform. -------------------------------------------------------------------------------- /4-terraform-advanced/2-for-each/terraform/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "rg" { 2 | for_each = toset(var.resource_group_names) 3 | name = each.key 4 | location = "uksouth" 5 | } 6 | -------------------------------------------------------------------------------- /1-terraform-basics/4-terraform-azure-provider/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Azure Provider 2 | 3 | Providers are neccessary to Terraform to successfully run and build resources - in this case, Azure resources! 4 | 5 | -------------------------------------------------------------------------------- /4-terraform-advanced/3-count/terraform/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "rg" { 2 | count = length(var.resource_group_names) 3 | name = var.resource_group_names[count.index] 4 | location = "uksouth" 5 | } -------------------------------------------------------------------------------- /1-terraform-basics/1-terraform-installation/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Installation 2 | 3 | The purpose of this tutorial is to setup the initial requirements to complete this lab/workshop. 4 | - Terraform installation 5 | - Visual Studio Code (Recommended) -------------------------------------------------------------------------------- /4-terraform-advanced/4-conditional-expressions/conditional-expressions-example/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" { 2 | type = string 3 | default = "tamopsrg" 4 | } 5 | 6 | variable "create_resource_group" { 7 | type = bool 8 | default = false 9 | } -------------------------------------------------------------------------------- /4-terraform-advanced/5-dynamic-blocks/terraform/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "rg" { 2 | dynamic "name" { 3 | for_each = var.resource_group_names 4 | content { 5 | name = name.value 6 | location = "uksouth" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /6-terraform-modules/terraform/main.tf: -------------------------------------------------------------------------------- 1 | module "acr" { 2 | source = "./modules/acr" 3 | 4 | resource_group_name = "tamopsrg" 5 | location = "UK South" 6 | acr_name = "tamopsacr" 7 | acr_sku = "Standard" 8 | acr_admin_enabled = true 9 | } -------------------------------------------------------------------------------- /3-terraform-state/scripts/2-delete-terraform-storage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #set -x 3 | 4 | # Deletes the relevant storage account to store terraform state 5 | 6 | RESOURCE_GROUP_NAME="deploy-first-rg" 7 | 8 | # Delete Resource Group 9 | 10 | az group delete -n $RESOURCE_GROUP_NAME 11 | -------------------------------------------------------------------------------- /3-terraform-state/local-state-example/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "local" { 3 | } 4 | 5 | required_providers { 6 | azurerm = { 7 | source = "hashicorp/azurerm" 8 | version = "3.98.0" 9 | } 10 | } 11 | 12 | } 13 | 14 | provider "azurerm" { 15 | features {} 16 | } -------------------------------------------------------------------------------- /3-terraform-state/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Installation 2 | 3 | The purpose of this tutorial is to deploy an example Terraform configuration to Azure. Firstly, using a local state and then configuring a remote state. 4 | 5 | ## Prerequisites 6 | 7 | - Azure Subscription 8 | - Terraform installated 9 | - Visual Studio Code (Recommended) 10 | - Azure CLI & logged into Azure 11 | -------------------------------------------------------------------------------- /6-terraform-modules/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "azurerm" { 3 | resource_group_name = "deploy-first-rg" 4 | storage_account_name = "deployfirsttamopssa" 5 | container_name = "acr" 6 | key = "terraform.tfstate" 7 | } 8 | 9 | required_providers { 10 | azurerm = { 11 | source = "hashicorp/azurerm" 12 | version = "3.98.0" 13 | } 14 | } 15 | 16 | } 17 | 18 | provider "azurerm" { 19 | features {} 20 | } -------------------------------------------------------------------------------- /3-terraform-state/remote-state-example/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "azurerm" { 3 | resource_group_name = "deploy-first-rg" 4 | storage_account_name = "deployfirsttamopssa" 5 | container_name = "tfstate" 6 | key = "terraform.tfstate" 7 | } 8 | 9 | required_providers { 10 | azurerm = { 11 | source = "hashicorp/azurerm" 12 | version = "3.98.0" 13 | } 14 | } 15 | 16 | } 17 | 18 | provider "azurerm" { 19 | features {} 20 | } -------------------------------------------------------------------------------- /4-terraform-advanced/3-count/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "azurerm" { 3 | resource_group_name = "deploy-first-rg" 4 | storage_account_name = "deployfirsttamopssa" 5 | container_name = "count" 6 | key = "terraform.tfstate" 7 | } 8 | 9 | required_providers { 10 | azurerm = { 11 | source = "hashicorp/azurerm" 12 | version = "3.98.0" 13 | } 14 | } 15 | 16 | } 17 | 18 | provider "azurerm" { 19 | features {} 20 | } -------------------------------------------------------------------------------- /4-terraform-advanced/2-for-each/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "azurerm" { 3 | resource_group_name = "deploy-first-rg" 4 | storage_account_name = "deployfirsttamopssa" 5 | container_name = "foreach" 6 | key = "terraform.tfstate" 7 | } 8 | 9 | required_providers { 10 | azurerm = { 11 | source = "hashicorp/azurerm" 12 | version = "3.98.0" 13 | } 14 | } 15 | 16 | } 17 | 18 | provider "azurerm" { 19 | features {} 20 | } -------------------------------------------------------------------------------- /4-terraform-advanced/1-depends-on/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "azurerm" { 3 | resource_group_name = "deploy-first-rg" 4 | storage_account_name = "deployfirsttamopssa" 5 | container_name = "dependson" 6 | key = "terraform.tfstate" 7 | } 8 | 9 | required_providers { 10 | azurerm = { 11 | source = "hashicorp/azurerm" 12 | version = "3.98.0" 13 | } 14 | } 15 | 16 | } 17 | 18 | provider "azurerm" { 19 | features {} 20 | } -------------------------------------------------------------------------------- /4-terraform-advanced/5-dynamic-blocks/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "azurerm" { 3 | resource_group_name = "deploy-first-rg" 4 | storage_account_name = "deployfirsttamopssa" 5 | container_name = "dynamicblock" 6 | key = "terraform.tfstate" 7 | } 8 | 9 | required_providers { 10 | azurerm = { 11 | source = "hashicorp/azurerm" 12 | version = "3.98.0" 13 | } 14 | } 15 | 16 | } 17 | 18 | provider "azurerm" { 19 | features {} 20 | } -------------------------------------------------------------------------------- /6-terraform-modules/terraform/modules/acr/main.tf: -------------------------------------------------------------------------------- 1 | # Resource Group 2 | resource "azurerm_resource_group" "rg" { 3 | name = var.resource_group_name 4 | location = var.location 5 | } 6 | 7 | # Azure Container Registry 8 | resource "azurerm_container_registry" "acr" { 9 | name = var.acr_name 10 | resource_group_name = azurerm_resource_group.rg.name 11 | location = azurerm_resource_group.rg.location 12 | sku = var.acr_sku 13 | admin_enabled = var.acr_admin_enabled 14 | } -------------------------------------------------------------------------------- /4-terraform-advanced/1-depends-on/terraform/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "rg" { 2 | name = var.resource_group_name 3 | location = "uksouth" 4 | } 5 | 6 | resource "azurerm_storage_account" "sa" { 7 | name = "tamopsstorage" 8 | resource_group_name = azurerm_resource_group.rg.name 9 | location = azurerm_resource_group.rg.location 10 | account_tier = "Standard" 11 | account_replication_type = "LRS" 12 | 13 | depends_on = [ 14 | azurerm_resource_group.rg 15 | ] 16 | } -------------------------------------------------------------------------------- /4-terraform-advanced/4-conditional-expressions/conditional-expressions-example/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "azurerm" { 3 | resource_group_name = "deploy-first-rg" 4 | storage_account_name = "deployfirsttamopssa" 5 | container_name = "conditional" 6 | key = "terraform.tfstate" 7 | } 8 | 9 | required_providers { 10 | azurerm = { 11 | source = "hashicorp/azurerm" 12 | version = "3.98.0" 13 | } 14 | } 15 | 16 | } 17 | 18 | provider "azurerm" { 19 | features {} 20 | } -------------------------------------------------------------------------------- /5-secret-management-azure/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "azurerm" { 3 | resource_group_name = "deploy-first-rg" 4 | storage_account_name = "deployfirsttamopssa" 5 | container_name = "count" 6 | key = "terraform.tfstate" 7 | } 8 | 9 | required_providers { 10 | azurerm = { 11 | source = "hashicorp/azurerm" 12 | version = "3.98.0" 13 | } 14 | } 15 | 16 | } 17 | 18 | provider "azurerm" { 19 | features {} 20 | } 21 | 22 | data "azurerm_client_config" "current" {} -------------------------------------------------------------------------------- /7-terraform-azapi/terraform/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "rg" { 2 | name = var.resource_group_name 3 | location = var.location 4 | } 5 | 6 | resource "azapi_resource" "acr" { 7 | location = azurerm_resource_group.rg.name 8 | type = "Microsoft.ContainerRegistry/registries@2020-11-01-preview" 9 | name = var.acr_name 10 | parent_id = azurerm_resource_group.rg.id 11 | body = jsonencode({ 12 | sku = { 13 | name = "Standard" 14 | } 15 | properties = { 16 | adminUserEnabled = true 17 | } 18 | }) 19 | } -------------------------------------------------------------------------------- /7-terraform-azapi/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" { 2 | type = string 3 | description = "The name of the resource group in which to create the container registry." 4 | default = "tamopsrg" 5 | } 6 | 7 | variable "location" { 8 | type = string 9 | description = "The Azure location where the container registry should exist." 10 | default = "uksouth" 11 | } 12 | 13 | variable "acr_name" { 14 | type = string 15 | description = "The name of the container registry." 16 | default = "tamopsacr" 17 | } 18 | -------------------------------------------------------------------------------- /7-terraform-azapi/terraform/providers.tf: -------------------------------------------------------------------------------- 1 | provider "azapi" { 2 | } 3 | 4 | terraform { 5 | backend "azurerm" { 6 | resource_group_name = "deploy-first-rg" 7 | storage_account_name = "deployfirsttamopssa" 8 | container_name = "azapi" 9 | key = "terraform.tfstate" 10 | } 11 | 12 | required_providers { 13 | azurerm = { 14 | source = "hashicorp/azurerm" 15 | version = "3.98.0" 16 | } 17 | azapi = { 18 | source = "azure/azapi" 19 | } 20 | } 21 | 22 | } 23 | 24 | provider "azurerm" { 25 | features {} 26 | } -------------------------------------------------------------------------------- /3-terraform-state/scripts/1-create-terraform-storage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #set -x 3 | 4 | # Creates the relevant storage account to store terraform state locally 5 | 6 | RESOURCE_GROUP_NAME="deploy-first-rg" 7 | STORAGE_ACCOUNT_NAME="deployfirsttamopssa" 8 | 9 | # Create Resource Group 10 | az group create -l uksouth -n $RESOURCE_GROUP_NAME 11 | 12 | # Create Storage Account 13 | az storage account create -n $STORAGE_ACCOUNT_NAME -g $RESOURCE_GROUP_NAME -l uksouth --sku Standard_LRS 14 | 15 | # Create Storage Account blob 16 | az storage container create --name tfstate --account-name $STORAGE_ACCOUNT_NAME -------------------------------------------------------------------------------- /4-terraform-advanced/4-conditional-expressions/conditional-expressions-example/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "rg" { 2 | name = var.resource_group_name 3 | location = "uksouth" 4 | } 5 | 6 | resource "azurerm_storage_account" "sa" { 7 | count = var.create_resource_group ? 1 : 0 8 | name = "tamopsstorage" 9 | resource_group_name = azurerm_resource_group.rg.name 10 | location = azurerm_resource_group.rg.location 11 | account_tier = "Standard" 12 | account_replication_type = "LRS" 13 | 14 | depends_on = [ 15 | azurerm_resource_group.rg 16 | ] 17 | } -------------------------------------------------------------------------------- /1-terraform-basics/1-terraform-installation/1-terraform-install.md: -------------------------------------------------------------------------------- 1 | # Terraform installation - How to 2 | 3 | Lets kick-off with the installation for Terraform 4 | 5 | ## Windows 6 | Recommend to use the Windows Package Manager [Chocolatey](https://chocolatey.org/), its great! 7 | - Once you have this configured, run the below: 8 | ```powershell 9 | choco install terraform 10 | ``` 11 | 12 | ## Mac 13 | Recommend to use the Mac Package Manager [HomeBrew](https://brew.sh/) 14 | - Once you have this configured, run the below: 15 | ```bash 16 | # Install the Hashicorp tap 17 | brew tap hashicorp/tap 18 | 19 | # Install Terraform 20 | brew install hashicorp/tap/terraform 21 | ``` -------------------------------------------------------------------------------- /6-terraform-modules/terraform/modules/acr/variables.tf: -------------------------------------------------------------------------------- 1 | variable "resource_group_name" { 2 | type = string 3 | description = "The name of the resource group in which to create the container registry." 4 | } 5 | 6 | variable "location" { 7 | type = string 8 | description = "The Azure location where the container registry should exist." 9 | } 10 | 11 | variable "acr_name" { 12 | type = string 13 | description = "The name of the container registry." 14 | } 15 | 16 | variable "acr_sku" { 17 | type = string 18 | description = "The SKU of the container registry." 19 | } 20 | 21 | variable "acr_admin_enabled" { 22 | type = bool 23 | description = "Should the admin user be enabled?" 24 | } -------------------------------------------------------------------------------- /1-terraform-basics/1-terraform-installation/2-vscode-install.md: -------------------------------------------------------------------------------- 1 | # Visual Studio Code Installation (Optional) 2 | 3 | I do recommend using Visual Code for code editing and working with this lab/workshop. 4 | 5 | ## Windows 6 | Recommend to use the Windows Package Manager [Chocolatey](https://chocolatey.org/), its great! 7 | - Once you have this configured, run the below: 8 | ```powershell 9 | choco install vscode 10 | ``` 11 | 12 | ## Mac 13 | Recommend to use the Mac Package Manager [HomeBrew](https://brew.sh/) 14 | - Once you have this configured, run the below: 15 | ```bash 16 | brew install --cask visual-studio-code 17 | ``` 18 | 19 | ## Recomended VSCode extensions 20 | 21 | The [HashiCorp Terraform Extension for Visual Studio Code (VS Code)](https://marketplace.visualstudio.com/items?itemName=HashiCorp.terraform) with the Terraform Language Server adds editing features for Terraform files such as syntax highlighting, IntelliSense, code navigation, code formatting, module explorer and much more! 22 | 23 | -------------------------------------------------------------------------------- /2-terraform-variables/3-terraform-local-variables.md: -------------------------------------------------------------------------------- 1 | # Terraform Local Variables 2 | 3 | In this section we will look at how to use local variables in Terraform. 4 | 5 | ## Terraform Local Variables - pros 6 | 7 | - Allows you to pass information into Terraform 8 | - Allows you to reuse the same Terraform configuration with different values 9 | 10 | ## Terraform Local Variables - cons 11 | 12 | - Requires additional configuration 13 | 14 | ## Terraform Local Variables - example 15 | 16 | In this example, we will be creating a resource group in Azure. We will be using a local variable to pass the name of the resource group into Terraform. 17 | 18 | ### Terraform Local Variables - example - variables.tf 19 | 20 | Creating variable files is a best practice, this allows you to keep all of your variables in one place. 21 | 22 | Variable `resource_group_name` is of type `string` and has a default value of `tamopsrg`. 23 | 24 | ```terraform 25 | 26 | variable "resource_group_name" { 27 | type = string 28 | default = "tamopsrg" 29 | } 30 | 31 | ``` -------------------------------------------------------------------------------- /3-terraform-state/1-terraform-state-local-vs-remote.md: -------------------------------------------------------------------------------- 1 | # Terraform State - Local vs Remote 2 | 3 | Terraform state is a file that is used to keep track of the resources that have been created by Terraform. This file is used to keep track of the resources that have been created, and also to keep track of the state of the resources that have been created. 4 | 5 | # Terraform local state 6 | 7 | Terraform local state is the default state that Terraform uses. This state is stored locally on the machine that Terraform is being run on. This state is stored in a file called `terraform.tfstate`. This file is stored in the same directory that Terraform is being run from. 8 | 9 | ## Terraform local state - pros 10 | 11 | - Simple to use 12 | - No additional configuration required 13 | 14 | ## Terraform local state - cons 15 | 16 | - Not recommended for production use 17 | - Not recommended for use with multiple users 18 | - Not recommended for use with multiple Terraform workspaces 19 | 20 | # Terraform remote state 21 | 22 | Terraform remote state is a state that is stored remotely. This state is stored in a remote location, in this case - it will be an Azure Storage Account. 23 | 24 | ## Terraform remote state - pros 25 | 26 | - Recommended for production use 27 | - Recommended for use with multiple users 28 | - Recommended for use with multiple Terraform workspaces 29 | 30 | ## Terraform remote state - cons 31 | 32 | - Requires additional configuration 33 | - Requires additional cost 34 | -------------------------------------------------------------------------------- /5-secret-management-azure/terraform/main.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_resource_group" "rg" { 2 | name = var.resource_group_name 3 | location = "uksouth" 4 | } 5 | 6 | resource "azurerm_key_vault" "kv" { 7 | name = "tamopskv" 8 | location = azurerm_resource_group.rg.location 9 | resource_group_name = azurerm_resource_group.rg.name 10 | tenant_id = data.azurerm_client_config.current.tenant_id 11 | sku_name = "standard" 12 | 13 | access_policy { 14 | tenant_id = data.azurerm_client_config.current.tenant_id 15 | object_id = data.azurerm_client_config.current.object_id 16 | 17 | secret_permissions = [ 18 | "Get", 19 | "List", 20 | "Set", 21 | "Delete" 22 | ] 23 | } 24 | } 25 | 26 | resource "azurerm_key_vault_secret" "sa" { 27 | name = "saname" 28 | value = "tamopsstoragekv" 29 | key_vault_id = azurerm_key_vault.kv.id 30 | } 31 | 32 | data "azurerm_key_vault_secret" "sa" { 33 | name = "saname" 34 | key_vault_id = azurerm_key_vault.kv.id 35 | depends_on = [ 36 | azurerm_key_vault_secret.sa 37 | ] 38 | } 39 | 40 | resource "azurerm_storage_account" "sa" { 41 | name = data.azurerm_key_vault_secret.sa.value 42 | resource_group_name = azurerm_resource_group.rg.name 43 | location = azurerm_resource_group.rg.location 44 | account_tier = "Standard" 45 | account_replication_type = "LRS" 46 | 47 | depends_on = [ 48 | azurerm_resource_group.rg 49 | ] 50 | } -------------------------------------------------------------------------------- /1-terraform-basics/2-terraform-commands/1-terraform-commands.md: -------------------------------------------------------------------------------- 1 | # Terraform Basic Commands 2 | 3 | Lets look at the basic commands you shuld know to begin working with and deploying Terraform. Throughout I may add some further but to begin, have a look at understanding of these! 4 | 5 | ## Terraform Version 6 | 7 | Check the version of Terraform you are running using: `terraform -v` 8 | 9 | ```bash 10 | terraform -v 11 | ``` 12 | 13 | ## Terraform Init 14 | 15 | Terraform init is used to initialise terarform and prepare your working directory and downtime the relevant providers etc. 16 | 17 | ```bash 18 | terraform init 19 | ``` 20 | 21 | ## Terraform Validate 22 | 23 | A great command to have, validates the terraform configuration in the current directory. Great to use in terms of checking syntax and configuration is correct. 24 | 25 | ```bash 26 | terraform validate 27 | ``` 28 | 29 | ## Terraform Plan 30 | 31 | Creates and shows a Plan of your Terraform configuration, this includes additions, updates and removals. 32 | 33 | ```bash 34 | terraform plan 35 | ``` 36 | 37 | ## Terraform apply 38 | 39 | Applies the Terraform configuration, in this case - it will be applying Terraform configuration that will deploy into Azure. 40 | 41 | ```bash 42 | terraform apply 43 | ``` 44 | 45 | ## Terraform Destroy 46 | 47 | Used to destroy a Terraform configuration, when you run this command successfully - it will also destroy the resources in Azure. 48 | 49 | ```bash 50 | terraform destroy 51 | ``` 52 | 53 | ## Terraform Format 54 | 55 | Formats current directory with Terraform/HCL standards. 56 | 57 | ```bash 58 | terraform fmt 59 | ``` -------------------------------------------------------------------------------- /1-terraform-basics/4-terraform-azure-provider/1-azure-provider.md: -------------------------------------------------------------------------------- 1 | ## What is a Terraform Provider? 2 | 3 | Terraform providers are plugins that allow Terraform to interact with the underlying API's of various services. In this case, we will be using the Azure Provider to interact with Azure. 4 | 5 | ## Why do we need a Terraform Provider? 6 | 7 | Providers are needed to allow Terraform to interact with the underlying API's of various services. In this case, we will be using the Azure Provider to interact with Azure. 8 | 9 | ## Azure Provider 10 | 11 | The Azure Provider is used to interact with the many resources supported by Azure Resource Manager (ARM). The provider needs to be configured with the proper credentials before it can be used. 12 | 13 | ## Azure Provider Configuration 14 | 15 | The Azure Provider is used to configure the proper credentials before it can be used. 16 | 17 | ### Azure Provider Configuration - Azure CLI 2.0 18 | 19 | The Azure Provider can be configured using the Azure CLI 2.0, which is installed as part of the Azure CLI 2.0. If you have already installed the Azure CLI 2.0, you can view the currently authenticated account name with the az account show command: 20 | 21 | ```bash 22 | az account show --query "{ subscription_id: id, tenant_id: tenantId }" 23 | ``` 24 | 25 | The output should be similar to the following: 26 | 27 | ```json 28 | { 29 | "subscription_id": "00000000-0000-0000-0000-000000000000", 30 | "tenant_id": "00000000-0000-0000-0000-000000000000" 31 | } 32 | ``` 33 | 34 | The subscription_id and tenant_id values can be used in the provider configuration block: 35 | 36 | ```terraform 37 | 38 | provider "azurerm" { 39 | features {} 40 | } 41 | 42 | ``` -------------------------------------------------------------------------------- /4-terraform-advanced/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Advanced 2 | 3 | In this section we will be looking at some more advanced Terraform concepts: 4 | 5 | - Depends On 6 | - For Each 7 | - Count 8 | - Conditional Expressions 9 | - Dynamic Blocks 10 | 11 | # Pre-requisites 12 | 13 | 1. Ensure remote storage account is configured for Terraform state. Each of the above, will contain a Terraform configuration that will be used to deploy resources to Azure to show its concept with a new state file for each. 14 | 15 | To do this, follow the steps in the [Terraform Remote State Deploy](https://github.com/thomast1906/terraform-on-azure/tree/main/2-terraform-state/3-terraform-remote-state-deploy.md) section. 16 | 17 | Please note, I will be using storage account name `deployfirsttamopssa` for the remote state storage account. You will need to change this to your own storage account name. 18 | 19 | Each will have providers.tf, to which you change the storage account name to your own. As shown below: 20 | 21 | ```terraform 22 | 23 | terraform { 24 | backend "azurerm" { 25 | resource_group_name = "deploy-first-rg" 26 | storage_account_name = "deployfirsttamopssa" 27 | container_name = "tfstate" 28 | key = "terraform.tfstate" 29 | } 30 | 31 | required_providers { 32 | azurerm = { 33 | source = "hashicorp/azurerm" 34 | version = "3.44.1" 35 | } 36 | } 37 | 38 | } 39 | 40 | provider "azurerm" { 41 | features {} 42 | } 43 | 44 | ``` 45 | 46 | 2. Update variables.tf with your own values. Example below shown as part of [1-depends-on](https://github.com/thomast1906/terraform-on-azure/tree/main/4-terraform-advanced/1-depends-on/1-depends-on-example/variables.tf) 47 | 48 | ```terraform 49 | 50 | variable "resource_group_name" { 51 | type = string 52 | default = "tamopsrg" 53 | } 54 | 55 | ``` 56 | 57 | -------------------------------------------------------------------------------- /2-terraform-variables/2-terraform-tfvars.md: -------------------------------------------------------------------------------- 1 | # Terraform tfvars 2 | 3 | tfvars files are used to store variables that are used in Terraform. These files are used to store sensitive information such as passwords and keys. These files are not stored in the repository and are ignored by git. 4 | 5 | ## Terraform tfvars - pros 6 | 7 | - Recommended for storing sensitive information 8 | - Recommended for storing information that is not to be shared 9 | 10 | ## Terraform tfvars - cons 11 | 12 | - Requires additional configuration 13 | - Requires additional cost 14 | 15 | ## Terraform tfvars - example 16 | 17 | In this example, we will be creating a resource group in Azure. We will be using a tfvars file to pass the name of the resource group into Terraform. 18 | 19 | ### Terraform tfvars - example - variables.tf 20 | 21 | Creating variable files is a best practice, this allows you to keep all of your variables in one place. 22 | 23 | Variable `resource_group_name` is of type `string` and has a default value of `tamopsrg`. 24 | 25 | ```terraform 26 | 27 | variable "resource_group_name" { 28 | type = string 29 | } 30 | 31 | ``` 32 | 33 | ### Terraform tfvars - example - terraform.tfvars 34 | 35 | Notice the reference to the variable `resource_group_name` in the tfvars file. This is used to pass the value `tamopsrg` to the variable `resource_group_name`. 36 | 37 | ```terraform 38 | 39 | resource_group_name = "tamopsrg" 40 | 41 | ``` 42 | 43 | ### Terraform tfvars - example - main.tf 44 | 45 | ```terraform 46 | 47 | resource "azurerm_resource_group" "rg" { 48 | name = var.resource_group_name 49 | location = "uksouth" 50 | } 51 | 52 | ``` 53 | 54 | ### Terraform tfvars - example - terraform init 55 | 56 | Running terraform with the tfvars file is done by using the `-tfvars-file` flag. example below 57 | 58 | ```terraform 59 | 60 | terraform plan -tfvars-file=terraform.tfvars 61 | 62 | ``` 63 | 64 | -------------------------------------------------------------------------------- /4-terraform-advanced/5-dynamic-blocks/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Dynamic Blocks 2 | 3 | ## Introduction 4 | 5 | Dynamic blocks are a new feature in Terraform 0.12 that allow you to create dynamic blocks of configuration within your Terraform configuration files. This is useful when you want to create a block of configuration that is repeated multiple times, but with different values for each instance. 6 | 7 | ## Terraform Dynamic Blocks - pros 8 | 9 | - Allows you to create multiple resources in a single resource block 10 | 11 | ## Terraform Dynamic Blocks - cons 12 | 13 | - Requires additional configuration 14 | 15 | ## Terraform Dynamic Blocks - example 16 | 17 | In this example, we will be creating a resource group in Azure. We will be using the `dynamic` argument to create multiple resource groups. 18 | 19 | ### Terraform Dynamic Blocks - example - variables.tf 20 | 21 | Creating variable files is a best practice, this allows you to keep all of your variables in one place. 22 | 23 | Variable `resource_group_names` is of type `list(string)` and has a default value of `["tamopsrg", "tamopsrg2"]`. 24 | 25 | ```terraform 26 | 27 | variable "resource_group_names" { 28 | type = list(string) 29 | default = ["tamopsrg", "tamopsrg2"] 30 | } 31 | 32 | ``` 33 | 34 | ### Terraform Dynamic Blocks - example - main.tf 35 | 36 | In this example, we are creating a resource group. We are using the dynamic argument to create multiple resource groups. 37 | 38 | ```terraform 39 | 40 | resource "azurerm_resource_group" "rg" { 41 | dynamic "name" { 42 | for_each = var.resource_group_names 43 | content { 44 | name = name.value 45 | location = "uksouth" 46 | } 47 | } 48 | } 49 | 50 | ``` 51 | 52 | ### Run example 53 | 54 | You can now run the example found in this section. 55 | 56 | Run Terraform from [here](https://github.com/thomast1906/terraform-on-azure/tree/main/4-terraform-advanced/5-dynamic-blocks/terraform) -------------------------------------------------------------------------------- /2-terraform-variables/1-terraform-variables.md: -------------------------------------------------------------------------------- 1 | # Terraform Variables 2 | 3 | Terraform Variables are a way to pass information into Terraform, this can be done in a number of ways, but the most common is to use a `variables.tf` file. 4 | 5 | ## Terraform Variables - pros 6 | 7 | - Allows you to pass information into Terraform 8 | - Allows you to reuse the same Terraform configuration with different values 9 | 10 | ## Terraform Variables - cons 11 | 12 | - Requires additional configuration 13 | 14 | ## Terraform Variables - example 15 | 16 | In this example, we will be creating a resource group in Azure. We will be using a variable to pass the name of the resource group into Terraform. 17 | 18 | ### Terraform Variables - example - variables.tf 19 | 20 | Creating variable files is a best practice, this allows you to keep all of your variables in one place. 21 | 22 | Variable `resource_group_name` is of type `string` and has a default value of `tamopsrg`. 23 | 24 | ```terraform 25 | 26 | variable "resource_group_name" { 27 | type = string 28 | default = "tamopsrg" 29 | } 30 | 31 | ``` 32 | 33 | ### Terraform Local Variables - example 34 | 35 | As below, using local variables is done by using the `var` keyword followed by the name of the variable. In this example, we are using the variable `resource_group_name` that is going to be ammended with a local variable. 36 | 37 | ### variables.tf 38 | 39 | ```terraform 40 | 41 | variable "resource_group_name" { 42 | type = string 43 | default = "tamopsrg" 44 | } 45 | 46 | ``` 47 | 48 | #### main.tf 49 | 50 | Ammending the variable `resource_group_name` to include "-local" using a local variable and referencing the local variable in the resource group. 51 | 52 | ```terraform 53 | 54 | locals { 55 | resource_group_name = "${var.resource_group_name}-local" 56 | } 57 | 58 | resource "azurerm_resource_group" "rg" { 59 | name = local.resource_group_name 60 | location = "uksouth" 61 | } 62 | 63 | ``` 64 | 65 | 66 | -------------------------------------------------------------------------------- /1-terraform-basics/3-terraform-resources/1-terraform-resources.md: -------------------------------------------------------------------------------- 1 | # Terraform Resources 2 | 3 | The primary deployments of Terraform is doing using one or more resources. They are the most commonly used component within Terraform. Each resource block will destroy at least one Azure resource. Resource blocks include virtual machines, virtual networks, resource groups etc. 4 | 5 | ## Resource example 6 | 7 | The below contains a resource called `rg` that uses the terraform resource `azurerm_resource_group` to create an Azure Resource Group called `tamops` in region `UK South` 8 | 9 | ```terraform 10 | resource "azurerm_resource_group" "rg" { 11 | name = "tamops" 12 | location = "UK South" 13 | } 14 | ``` 15 | 16 | Read further on resource usage [here](https://developer.hashicorp.com/terraform/language/resources/syntax) 17 | 18 | ## Data source example 19 | 20 | If the above Azure Resource Group `tamops` has already been created previously or within another Terraform deployment, utilising the Data source - you can make reference to the resource group. 21 | 22 | The below references the data resource `azurerm_resource_group` `rg` to deploy Azure Storage Account `tamopsstorage` into. 23 | - notice the reference of `resource_group_name` & `location` is using the data reference of Resource Group? 24 | 25 | ```terraform 26 | data "azurerm_resource_group" "rg" { 27 | name = "tamops" 28 | } 29 | 30 | resource "azurerm_storage_account" "sa" { 31 | name = "tamopsstorage" 32 | resource_group_name = data.azurerm_resource_group.rg.name 33 | location = data.azurerm_resource_group.rg.location 34 | account_tier = "Standard" 35 | account_replication_type = "LRS" 36 | } 37 | } 38 | ``` 39 | 40 | Each resource and data resource has various outputs available, [here](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/resource_group.html#attributes-reference) shows the outputs available for the above data resource. -------------------------------------------------------------------------------- /4-terraform-advanced/1-depends-on/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Depends On 2 | []: # 3 | []: # A guide on how to use 4 | 5 | In this section we will be looking at the depends_on argument in Terraform. 6 | 7 | ## Terraform Depends On - pros 8 | 9 | - Allows you to control the order in which resources are created 10 | 11 | ## Terraform Depends On - cons 12 | 13 | - Requires additional configuration 14 | 15 | ## Terraform Depends On - example 16 | 17 | In this example, we will be creating a resource group in Azure. We will be using the `depends_on` argument to control the order in which resources are created. 18 | 19 | ### Terraform Depends On - example - variables.tf 20 | 21 | Creating variable files is a best practice, this allows you to keep all of your variables in one place. 22 | 23 | Variable `resource_group_name` is of type `string` and has a default value of `tamopsrg`. 24 | 25 | ```terraform 26 | 27 | variable "resource_group_name" { 28 | type = string 29 | default = "tamopsrg" 30 | } 31 | 32 | ``` 33 | 34 | ### Terraform Depends On - example - main.tf 35 | 36 | In this example, we are creating a resource group and a storage account. We are using the depends_on argument to control the order in which resources are created. 37 | 38 | ```terraform 39 | 40 | resource "azurerm_resource_group" "rg" { 41 | name = var.resource_group_name 42 | location = "uksouth" 43 | } 44 | 45 | resource "azurerm_storage_account" "sa" { 46 | name = "tamopsstorage" 47 | resource_group_name = azurerm_resource_group.rg.name 48 | location = azurerm_resource_group.rg.location 49 | account_tier = "Standard" 50 | account_replication_type = "LRS" 51 | 52 | depends_on = [ 53 | azurerm_resource_group.rg 54 | ] 55 | } 56 | 57 | ``` 58 | 59 | ### Run example 60 | 61 | You can now run the example found in this section. 62 | 63 | Run Terraform from [here](https://github.com/thomast1906/terraform-on-azure/tree/main/4-terraform-advanced/1-depends-on/terraform) 64 | -------------------------------------------------------------------------------- /3-terraform-state/3-terraform-remote-state-deploy.md: -------------------------------------------------------------------------------- 1 | # Terraform Deploy using Remote State file 2 | 3 | Review directory [remote-state-example](https://github.com/thomast1906/terraform-on-azure/tree/main/2-terraform-state/remote-state-example) and the Terraform configuration files. 4 | 5 | ## Create remote state storage account 6 | 7 | Create a storage account to store the remote state file. 8 | 1. Edit the [variables](https://github.com/thomast1906/terraform-on-azure/tree/main/2-terraform-state/scripts/1-create-terraform-storage.sh#L6-L7) 9 | 2. Run the script `./scripts/1-create-terraform-storage.sh` 10 | 3. The script will create 11 | - Azure Resource Group 12 | - Azure Storage Account 13 | - Azure Blob storage location within Azure Storage Account 14 | 4. Successful script run will create a storage account with blob ready to state terraform state file 15 | 16 | ## Terraform init 17 | 18 | Run the following command to initialise Terraform when in the remote-state-example directory: 19 | 20 | ```terraform 21 | terraform init 22 | ``` 23 | 24 | ## Terraform plan 25 | 26 | Run the following command to create and show a plan of your Terraform configuration: 27 | 28 | ```terraform 29 | terraform plan 30 | ``` 31 | 32 | The plan will be similar to the previous local state example, but the state file will be stored in the Azure Storage Account rather than local, as shown in image below: 33 | 34 | ![Terraform plan](/images/terraform-plan.png) 35 | 36 | ## Terraform apply 37 | 38 | Run the following command to apply the Terraform configuration: 39 | 40 | ```terraform 41 | terraform apply 42 | ``` 43 | 44 | Review the apply this time, remote state stored - but will deploy the similar resources as the local state example. 45 | 46 | ## Terraform destroy 47 | 48 | Run the following command to destroy the Terraform configuration: 49 | 50 | ```terraform 51 | terraform destroy 52 | ``` 53 | 54 | ## Terraform destroy - remote state storage account 55 | 56 | Run the following command to destroy the remote state storage account: 57 | 58 | 1. Edit the [variables](https://github.com/thomast1906/terraform-on-azure/tree/main/2-terraform-state/scripts/2-delete-terraform-storage.sh#L6) 59 | 2. Run the script `./scripts/2-delete-terraform-storage.sh` 60 | -------------------------------------------------------------------------------- /4-terraform-advanced/2-for-each/README.md: -------------------------------------------------------------------------------- 1 | # Terraform For Each 2 | []: # 3 | []: # A guide on how to use 4 | 5 | In this section we will be looking at the for_each argument in Terraform. 6 | 7 | ## Terraform For Each - pros 8 | 9 | - Allows you to create multiple resources in a single resource block 10 | - Allows you to create multiple resources in a single resource block 11 | 12 | ## Terraform For Each - cons 13 | 14 | - Requires additional configuration 15 | 16 | ## Terraform For Each - example 17 | 18 | In this example, we will be creating a resource group in Azure. We will be using the `for_each` argument to create multiple resource groups. 19 | 20 | ### Terraform For Each - example - variables.tf 21 | 22 | Creating variable files is a best practice, this allows you to keep all of your variables in one place. 23 | 24 | Variable `resource_group_names` is of type `list(string)` and has a default value of `["tamopsrg", "tamopsrg2"]`. 25 | 26 | ```terraform 27 | 28 | variable "resource_group_names" { 29 | type = list(string) 30 | default = ["tamopsrg", "tamopsrg2"] 31 | } 32 | 33 | ``` 34 | 35 | ### Terraform For Each - example - main.tf 36 | 37 | In this example, we are creating a resource group. We are using the for_each argument to create multiple resource groups. 38 | 39 | ```terraform 40 | 41 | resource "azurerm_resource_group" "rg" { 42 | for_each = toset(var.resource_group_names) 43 | name = each.key 44 | location = "uksouth" 45 | } 46 | 47 | ``` 48 | 49 | ### Terraform For Each - example - output.tf 50 | 51 | In this example, we are creating an output. We are using the for_each argument to create multiple outputs. 52 | 53 | ```terraform 54 | 55 | output "resource_group_names" { 56 | value = azurerm_resource_group.rg[*].name 57 | } 58 | 59 | ``` 60 | 61 | ### Terraform For Each - example - output 62 | 63 | ```bash 64 | 65 | Outputs: 66 | 67 | resource_group_names = [ 68 | "tamopsrg", 69 | "tamopsrg2", 70 | ] 71 | 72 | ``` 73 | 74 | ## Terraform For Each - further reading 75 | 76 | - [Terraform for_each](https://www.terraform.io/docs/language/meta-arguments/for_each.html) 77 | 78 | ### Run example 79 | 80 | You can now run the example found in this section. 81 | 82 | Run Terraform from [here](https://github.com/thomast1906/terraform-on-azure/tree/main/4-terraform-advanced/2-for-each/terraform) 83 | -------------------------------------------------------------------------------- /4-terraform-advanced/4-conditional-expressions/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Conditions 2 | 3 | In this section we will look at how to use conditions in Terraform. 4 | 5 | ## Terraform Conditions - pros 6 | 7 | - Allows you to control the order in which resources are created 8 | 9 | ## Terraform Conditions - cons 10 | 11 | - Requires additional configuration 12 | 13 | ## Terraform Conditions - example 14 | 15 | A conditional expression uses the value of a boolean expression to select one of two values. 16 | 17 | In this example, we will be creating a resource group in Azure. We will be using a conditional expression to control the order in which resources are created. 18 | 19 | ### Terraform Conditions - example - variables.tf 20 | 21 | Creating variable files is a best practice, this allows you to keep all of your variables in one place. 22 | 23 | Variable `resource_group_name` is of type `string` and has a default value of `tamopsrg`. 24 | 25 | ```terraform 26 | 27 | variable "resource_group_name" { 28 | type = string 29 | default = "tamopsrg" 30 | } 31 | 32 | variable "create_resource_group" { 33 | type = bool 34 | default = false 35 | } 36 | 37 | ``` 38 | 39 | 40 | ### Terraform Conditions - example - main.tf 41 | 42 | In this example, we are creating a resource group and a storage account. We are using a conditional expression to determine whether or not to create the resource group. 43 | 44 | 1 returns true, 0 returns false. 45 | 46 | Change the value of `create_resource_group` to `true` to create the resource group. 47 | 48 | ```terraform 49 | 50 | resource "azurerm_resource_group" "rg" { 51 | name = var.resource_group_name 52 | location = "uksouth" 53 | } 54 | 55 | resource "azurerm_storage_account" "sa" { 56 | count = var.create_resource_group ? 1 : 0 57 | name = "tamopsstorage" 58 | resource_group_name = azurerm_resource_group.rg.name 59 | location = azurerm_resource_group.rg.location 60 | account_tier = "Standard" 61 | account_replication_type = "LRS" 62 | 63 | depends_on = [ 64 | azurerm_resource_group.rg 65 | ] 66 | } 67 | 68 | ``` 69 | 70 | ### Run example 71 | 72 | You can now run the example found in this section. 73 | 74 | Run Terraform from [here](https://github.com/thomast1906/terraform-on-azure/tree/main/4-terraform-advanced/4-conditional-expressions/terraform) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform on Azure 2 | A repo self-lead to give you an understanding on deploying Terraform on Azure from the basics to deploying into Azure. 3 | 4 | ## What you will learn 5 | 6 | In this self-lead, you will learn how to deploy Terraform on Azure from the basics to deploying into Azure. 7 | 8 | This self-lead journey is broken down into 7 sections: 9 | 10 | 1. Terraform Basics 11 | - Terraform Installation 12 | - Terraform Basic Commands 13 | - Introduction to Terraform resources 14 | - Using Terraform Azure Provider 15 | 2. Terraform Variables 16 | - Introduction to Terraform Variables 17 | - Terraform tfvars 18 | - Local Variables 19 | 3. Terraform State 20 | - Introduction to Terraform State 21 | - Local vs Remote State 22 | 4. Terraform Advanced 23 | - Depends On 24 | - For Each 25 | - Count 26 | - Conditional Expressions 27 | - Dynamic Blocks 28 | 5. Secret Management in Azure 29 | - Introduction to Azure Key Vault 30 | - Introduction to Azure Key Vault Secrets 31 | - Deploying Secrets to Azure Key Vault using Terraform 32 | 6. Terraform Modules 33 | - Introduction to Terraform Modules 34 | - Using Terraform Modules & deploying 35 | 7. Terraform AzAPI 36 | - Introduction to Terraform AzAPI 37 | - Using Terraform AzAPI & deploying 38 | 39 | 40 | ## Prerequisites 41 | 42 | - [Azure Account](https://azure.microsoft.com/en-us/free/) 43 | 44 | ## How to use this repo / self-lead format 45 | 46 | This repo is broken down into 7 sections, each section has a README.md file that will guide you through the self-lead. Each section may have a number of README.md files that will guide you through the section. These are also numbered to help you navigate. 47 | 48 | Please do start from the beginning and work your way through the self-lead setup. Starting at 1 and working your way through to 7. 49 | 50 | As you work through the self-lead, you will be asked to run various terraform. Please ensure you are in the correct `terraform `directory when running the terraform commands. (You will learn how to do this in section 3 [terraform state](https://github.com/thomast1906/terraform-on-azure/tree/main/3-terraform-state)) - Also remember to destroy your resources when you are done in each section. 51 | 52 | ## Request a new tutorial / feature in this repo 53 | 54 | Please do reach out to me or raise an issue on the GitHub repo if you are wanting me to include additional content - I feel it will be a continous work in progress to give you an understanding of Terraform while giving you the ability to deploy resources into Azure! 55 | 56 | 57 | -------------------------------------------------------------------------------- /4-terraform-advanced/3-count/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Count 2 | 3 | In this section we will look at the count parameter in Terraform. This is a very useful parameter that allows you to create multiple resources of the same type. This is useful for creating multiple VMs, multiple NICs, multiple disks etc. 4 | 5 | ## Terraform Count - pros 6 | 7 | - Allows you to create multiple resources in a single resource block 8 | 9 | ## Terraform Count - cons 10 | 11 | - Requires additional configuration 12 | - Sometimes you need to use the `count.index` to reference the correct resource, this can be confusing and sometimes not recommended. It is worth adding this section, but I do much prefer using `depends_on` if you can 13 | 14 | ## Terraform Count - example 15 | 16 | In this example, we will be creating a resource group in Azure. We will be using the `count` parameter to create multiple resource groups. 17 | 18 | ### Terraform Count - example - variables.tf 19 | 20 | Creating variable files is a best practice, this allows you to keep all of your variables in one place. 21 | 22 | Variable `resource_group_names` is of type `list(string)` and has a default value of `["tamopsrg", "tamopsrg2"]`. 23 | 24 | ```terraform 25 | 26 | variable "resource_group_names" { 27 | type = list(string) 28 | default = ["tamopsrg", "tamopsrg2"] 29 | } 30 | 31 | ``` 32 | 33 | ### Terraform Count - example - main.tf 34 | 35 | In this example, we are creating a resource group. We are using the count parameter to create multiple resource groups. 36 | 37 | ```terraform 38 | 39 | resource "azurerm_resource_group" "rg" { 40 | count = length(var.resource_group_names) 41 | name = var.resource_group_names[count.index] 42 | location = "uksouth" 43 | } 44 | 45 | ``` 46 | 47 | ### Terraform Count - example - output.tf 48 | 49 | In this example, we are creating an output. We are using the count parameter to create multiple outputs. 50 | 51 | ```terraform 52 | 53 | output "resource_group_names" { 54 | value = azurerm_resource_group.rg[*].name 55 | } 56 | 57 | ``` 58 | 59 | ### Terraform Count - example - output 60 | 61 | ```bash 62 | 63 | Outputs: 64 | 65 | resource_group_names = [ 66 | "tamopsrg", 67 | "tamopsrg2", 68 | ] 69 | 70 | ``` 71 | 72 | ### Terraform Count - example - output second resource group 73 | 74 | ```terraform 75 | 76 | output "resource_group_names" { 77 | value = azurerm_resource_group.rg[1].name 78 | } 79 | 80 | ``` 81 | 82 | ### Terraform Count - example - output second resource group 83 | 84 | ```bash 85 | 86 | Outputs: 87 | 88 | resource_group_names = "tamopsrg2" 89 | 90 | ``` 91 | 92 | ### Run example 93 | 94 | You can now run the example found in this section. 95 | 96 | Run Terraform from [here](https://github.com/thomast1906/terraform-on-azure/tree/main/4-terraform-advanced/3-count/terraform) -------------------------------------------------------------------------------- /3-terraform-state/2-terraform-local-state-deploy.md: -------------------------------------------------------------------------------- 1 | # Terraform Deploy using Local State file 2 | 3 | Review directory [local-state-example](https://github.com/thomast1906/terraform-on-azure/tree/main/2-terraform-state/local-state-example) and the Terraform configuration files. 4 | 5 | Notice the reference to terraform backend in provider.tf 6 | 7 | ```terraform 8 | terraform { 9 | backend "local" { 10 | } 11 | } 12 | ``` 13 | 14 | This is telling Terraform to use the local state file. 15 | 16 | ## Terraform init 17 | 18 | Run the following command to initialise Terraform when in the local-state-example directory: 19 | 20 | ```terraform 21 | terraform init 22 | ``` 23 | 24 | ## Terraform plan 25 | 26 | Run the following command to create and show a plan of your Terraform configuration: 27 | 28 | ```terraform 29 | terraform plan 30 | ``` 31 | 32 | A successful plan will look like the following: 33 | 34 | ```terraform 35 | thomast@MGH97DTW40 local-state-example % terraform plan 36 | 37 | Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 38 | + create 39 | 40 | Terraform will perform the following actions: 41 | 42 | # azurerm_resource_group.rg will be created 43 | + resource "azurerm_resource_group" "rg" { 44 | + id = (known after apply) 45 | + location = "uksouth" 46 | + name = "tamopslocalrg" 47 | } 48 | 49 | Plan: 1 to add, 0 to change, 0 to destroy. 50 | ``` 51 | 52 | ## Terraform apply 53 | 54 | Run the following command to apply the Terraform configuration: 55 | 56 | ```terraform 57 | terraform apply 58 | ``` 59 | 60 | A successful apply will look like the following: 61 | 62 | ```terraform 63 | thomast@MGH97DTW40 local-state-example % terraform apply 64 | 65 | Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: 66 | + create 67 | 68 | Terraform will perform the following actions: 69 | 70 | # azurerm_resource_group.rg will be created 71 | + resource "azurerm_resource_group" "rg" { 72 | + id = (known after apply) 73 | + location = "uksouth" 74 | + name = "tamopslocalrg" 75 | } 76 | 77 | Plan: 1 to add, 0 to change, 0 to destroy. 78 | ``` 79 | 80 | Review the output of the apply command, notice the following: 81 | 82 | ```terraform 83 | Apply complete! Resources: 1 added, 0 changed, 0 destroyed. 84 | ``` 85 | 86 | ## Terraform destroy 87 | 88 | Run the following command to destroy the Terraform configuration: 89 | 90 | ```terraform 91 | terraform destroy 92 | ``` 93 | 94 | A successful destroy will look like the following: 95 | 96 | ```terraform 97 | thomast@MGH97DTW40 local-state-example % terraform destroy 98 | azurerm_resource_group.rg: Refreshing state... [id=/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/tamopslocalrg] 99 | azurerm_resource_group.rg: Destroying... [id=/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/tamopslocalrg] 100 | azurerm_resource_group.rg: Destruction complete after 1s 101 | 102 | Destroy complete! Resources: 1 destroyed. 103 | ``` 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /7-terraform-azapi/README.md: -------------------------------------------------------------------------------- 1 | # Terraform AzAPI 2 | 3 | A few months ago, there was an announcement of AzAPI Terraform provider, that enables you to manage any Azure resource type using any API version. You may be wondering, why the need for this? 4 | 5 | Some times with new resource updates or additions, the Terraform AzureRM provider is not up to date or missing a piece of functionality within a particular resource. With this new provider, you can begin deploying using Terraform from day 1 without the need to wait until AzureRM has been updated – awesome! 6 | 7 | In this section, we will be looking at how to use the AzAPI provider to deploy resources to Azure. 8 | 9 | # Terraform AzAPI - Pros 10 | 11 | - You can deploy any Azure resource type using any API version 12 | - You can deploy resources that are not yet supported by the AzureRM provider 13 | 14 | # Terraform AzAPI - Cons 15 | 16 | - You need to know the API version of the resource you are deploying 17 | 18 | # Terraform AzAPI - example 19 | 20 | In this example, we will be deploying a new Azure Container Registry (ACR) using the AzAPI provider. 21 | 22 | Example below of the Terraform configuration files: 23 | 24 | providers.tf - this is the provider configuration file, showing the AzAPI provider information required. 25 | 26 | ```terraform 27 | 28 | provider "azapi" { 29 | } 30 | 31 | terraform { 32 | backend "local" { 33 | resource_group_name = "deploy-first-rg" 34 | storage_account_name = "deployfirsttamopssa" 35 | container_name = "azapi" 36 | key = "terraform.tfstate" 37 | } 38 | 39 | required_providers { 40 | azurerm = { 41 | source = "hashicorp/azurerm" 42 | version = "3.44.1" 43 | } 44 | azapi = { 45 | source = "azure/azapi" 46 | } 47 | } 48 | 49 | } 50 | 51 | provider "azurerm" { 52 | features {} 53 | } 54 | 55 | ``` 56 | 57 | 58 | main.tf 59 | 60 | ```terraform 61 | resource "azurerm_resource_group" "rg" { 62 | name = var.resource_group_name 63 | location = var.location 64 | } 65 | 66 | resource "azapi_resource" "acr" { 67 | location = azurerm_resource_group.rg.name 68 | type = "Microsoft.ContainerRegistry/registries@2020-11-01-preview" 69 | name = var.acr_name 70 | parent_id = azurerm_resource_group.rg.id 71 | body = jsonencode({ 72 | sku = { 73 | name = "Standard" 74 | } 75 | properties = { 76 | adminUserEnabled = true 77 | } 78 | }) 79 | } 80 | 81 | ``` 82 | 83 | variables.tf 84 | 85 | ```terraform 86 | 87 | variable "resource_group_name" { 88 | type = string 89 | description = "The name of the resource group in which to create the container registry." 90 | default = "tamopsrg" 91 | } 92 | 93 | variable "location" { 94 | type = string 95 | description = "The Azure location where the container registry should exist." 96 | default = "uksouth" 97 | } 98 | 99 | variable "acr_name" { 100 | type = string 101 | description = "The name of the container registry." 102 | default = "tamopsacr" 103 | } 104 | 105 | 106 | ``` 107 | 108 | ### Run example 109 | 110 | You can now run the example found in this section. 111 | 112 | Run Terraform from [here](https://github.com/thomast1906/terraform-on-azure/tree/main/7-terraform-azapi/terraform) 113 | -------------------------------------------------------------------------------- /5-secret-management-azure/README.md: -------------------------------------------------------------------------------- 1 | # Terraform secret management in Azure 2 | 3 | Deploying resources in Azure, you will often need to pass sensitive information to the resources. This could be a password, a certificate or a key. In this tutorial, we will look at how to manage secrets in Terraform using an Azure Key Vault. 4 | 5 | ## Terraform secret management in Azure - pros 6 | 7 | - Recommended for storing sensitive information 8 | - Recommended for storing information that is not to be shared 9 | 10 | ## Terraform secret management in Azure - cons 11 | 12 | - Requires additional configuration 13 | 14 | ## Terraform secret management in Azure - example 15 | 16 | In this example, we will be creating a resource group in Azure. We will be using an Azure Key Vault to store the name of the storage account. 17 | 18 | ### Terraform secret management - example - variables.tf 19 | 20 | Variable `resource_group_name` is of type `string` and has a default value of `tamopsrg`. 21 | 22 | ```terraform 23 | 24 | variable "resource_group_name" { 25 | type = string 26 | default = "tamopsrg" 27 | } 28 | 29 | ``` 30 | 31 | ### Terraform secret management in Azure - example - main.tf 32 | 33 | Storage Account name is stored in a Key Vault. The Key Vault is created in the same Terraform configuration. The Key Vault is created with an access policy that allows the current user to get the secret. The secret is then used to create the storage account. 34 | 35 | ```terraform 36 | 37 | resource "azurerm_resource_group" "rg" { 38 | name = var.resource_group_name 39 | location = "uksouth" 40 | } 41 | 42 | resource "azurerm_key_vault" "kv" { 43 | name = "tamopskv" 44 | location = azurerm_resource_group.rg.location 45 | resource_group_name = azurerm_resource_group.rg.name 46 | tenant_id = data.azurerm_client_config.current.tenant_id 47 | sku_name = "standard" 48 | 49 | access_policy { 50 | tenant_id = data.azurerm_client_config.current.tenant_id 51 | object_id = data.azurerm_client_config.current.object_id 52 | 53 | secret_permissions = [ 54 | "Get", 55 | "List", 56 | "Set", 57 | "Delete" 58 | ] 59 | } 60 | } 61 | 62 | resource "azurerm_key_vault_secret" "sa" { 63 | name = "saname" 64 | value = "tamopsstoragekv" 65 | key_vault_id = azurerm_key_vault.kv.id 66 | } 67 | 68 | data "azurerm_key_vault_secret" "sa" { 69 | name = "saname" 70 | key_vault_id = azurerm_key_vault.kv.id 71 | depends_on = [ 72 | azurerm_key_vault_secret.sa 73 | ] 74 | } 75 | 76 | resource "azurerm_storage_account" "sa" { 77 | name = data.azurerm_key_vault_secret.sa.value 78 | resource_group_name = azurerm_resource_group.rg.name 79 | location = azurerm_resource_group.rg.location 80 | account_tier = "Standard" 81 | account_replication_type = "LRS" 82 | 83 | depends_on = [ 84 | azurerm_resource_group.rg 85 | ] 86 | } 87 | 88 | ``` 89 | 90 | Notice the use of data keyvault secret to get the secret from the Key Vault to be used as name of the storage account. 91 | 92 | ```terraform 93 | data "azurerm_key_vault_secret" "sa" { 94 | name = "saname" 95 | key_vault_id = azurerm_key_vault.kv.id 96 | depends_on = [ 97 | azurerm_key_vault_secret.sa 98 | ] 99 | } 100 | 101 | resource "azurerm_storage_account" "sa" { 102 | name = data.azurerm_key_vault_secret.sa.value 103 | resource_group_name = azurerm_resource_group.rg.name 104 | location = azurerm_resource_group.rg.location 105 | account_tier = "Standard" 106 | account_replication_type = "LRS" 107 | 108 | depends_on = [ 109 | azurerm_resource_group.rg 110 | ] 111 | } 112 | 113 | ``` 114 | 115 | ### Run example 116 | 117 | You can now run the example found in this section. 118 | 119 | Run Terraform from [here](https://github.com/thomast1906/terraform-on-azure/tree/main/5-secret-management-azure/terraform) -------------------------------------------------------------------------------- /6-terraform-modules/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Modules 2 | 3 | A module is a collection of resources that are used together. Modules are used to create reusable components, increase readability, and to organize infrastructure as code. 4 | 5 | Writing Terraform, like any other IaC toolset, over time you may be repeating the same process for common resources such as an Azure Virtual network, Container registry, Postgres Database etc – instead of copying the same resource multiple times, you can create what is called a Terraform module to assist you with this repetition allowing you to create reusable Terraform. 6 | 7 | Modules are a great way to create reusable components, increase readability, and to organize infrastructure as code. 8 | 9 | ## Terraform Module - Pros 10 | 11 | - Reusable 12 | - Organised 13 | - Readable 14 | - Maintainable 15 | - Versioned 16 | 17 | ## Terraform Module - Cons 18 | 19 | - Complexity 20 | 21 | ## Terraform Module Structure 22 | 23 | A Terraform module is a directory that contains Terraform configuration files. The directory structure of a module is as follows: 24 | 25 | What can be included as part of a Terraform module? 26 | 27 | - Terraform Resources – That deploy whenever you reference your module within Terraform 28 | - Terraform Inputs – From your main Terraform deployment you will input various values and configurations that will be referenced within your Terraform module 29 | - Terraform Outputs – Outputs that can be used once the module is deployed, for example the resource ID 30 | - Whatever else you want to include 🙂 – What else you want to include can be decided by you and each module is certainly different! 31 | 32 | ## Standard Terraform Deployment 33 | 34 | The below is a standard Terraform deployment that deploys an `Azure Resource Group` and `Azure Container Registry` 35 | 36 | ```terraform 37 | 38 | # Resource Group 39 | resource "azurerm_resource_group" "rg" { 40 | name = "tamops" 41 | location = "UK South" 42 | } 43 | 44 | # Azure Container Registry 45 | 46 | resource "azurerm_container_registry" "acr" { 47 | name = "tamopsacr" 48 | resource_group_name = azurerm_resource_group.rg.name 49 | location = azurerm_resource_group.rg.location 50 | sku = "Standard" 51 | admin_enabled = true 52 | } 53 | 54 | ``` 55 | 56 | How can we make this more reusable? (This is a lightweight example) – We can create a Terraform module that will deploy the above resources. Allowing us to reference the module within our Terraform deployment and reuse the module as many times as we want. 57 | 58 | ## Terraform Module Deployment 59 | 60 | ### Terraform Module - Directory Structure 61 | 62 | The below is the directory structure of a Terraform module, basic structure that can follow similar that you have created a standard Terraform deployment. 63 | 64 | ```bash 65 | ├── main.tf 66 | ├── outputs.tf 67 | ├── variables.tf 68 | ``` 69 | 70 | ### Terraform Module - main.tf 71 | 72 | The below is the `main.tf` file that will contain the resources that will be deployed when you reference the module within your Terraform deployment. 73 | 74 | ```terraform 75 | 76 | # Resource Group 77 | resource "azurerm_resource_group" "rg" { 78 | name = var.resource_group_name 79 | location = var.location 80 | } 81 | 82 | # Azure Container Registry 83 | 84 | resource "azurerm_container_registry" "acr" { 85 | name = var.acr_name 86 | resource_group_name = azurerm_resource_group.rg.name 87 | location = azurerm_resource_group.rg.location 88 | sku = var.acr_sku 89 | admin_enabled = var.acr_admin_enabled 90 | } 91 | 92 | ``` 93 | 94 | ### Terraform Module - variables.tf 95 | 96 | The below is the `variables.tf` file that will contain the variables that will be referenced within the `main.tf` file. 97 | 98 | ```terraform 99 | 100 | variable "resource_group_name" { 101 | type = string 102 | description = "The name of the resource group in which to create the container registry." 103 | } 104 | 105 | variable "location" { 106 | type = string 107 | description = "The Azure location where the container registry should exist." 108 | } 109 | 110 | variable "acr_name" { 111 | type = string 112 | description = "The name of the container registry." 113 | } 114 | 115 | variable "acr_sku" { 116 | type = string 117 | description = "The SKU of the container registry." 118 | } 119 | 120 | variable "acr_admin_enabled" { 121 | type = bool 122 | description = "Should the admin user be enabled?" 123 | } 124 | 125 | ``` 126 | 127 | ### Terraform Module - outputs.tf 128 | 129 | The below is the `outputs.tf` file that will contain the outputs that will be referenced within the `main.tf` file. 130 | 131 | ```terraform 132 | 133 | output "acr_id" { 134 | value = azurerm_container_registry.acr.id 135 | } 136 | 137 | ``` 138 | 139 | ### Terraform Module - Reference 140 | 141 | The below is the `main.tf` file that will reference the module within your Terraform deployment. 142 | 143 | ```terraform 144 | 145 | module "acr" { 146 | source = "./modules/acr" 147 | 148 | resource_group_name = "tamops" 149 | location = "UK South" 150 | acr_name = "tamopsacr" 151 | acr_sku = "Standard" 152 | acr_admin_enabled = true 153 | } 154 | 155 | ``` 156 | 157 | ### Run example 158 | 159 | You can now run the example found in this section. 160 | 161 | Run Terraform from [here](https://github.com/thomast1906/terraform-on-azure/tree/main/4-terraform-advanced/6-terraform-modules/terraform) -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | # Copilot Instructions for terraform-on-azure Repository 2 | 3 | **You MUST start every interaction with: "I'll help you work with this Terraform learning repository."** 4 | 5 | This repository is a comprehensive 7-section self-guided learning path for Terraform on Azure. This file provides detailed instructions for GitHub Copilot to effectively assist users working through the learning materials. 6 | 7 | ## Repository Structure Overview 8 | 9 | This is a learning repository with 7 progressive sections: 10 | 1. **1-terraform-basics** - Installation, commands, resources, Azure provider 11 | 2. **2-terraform-variables** - Variables, tfvars, local variables 12 | 3. **3-terraform-state** - Local vs remote state management 13 | 4. **4-terraform-advanced** - Depends on, for each, count, conditionals, dynamic blocks 14 | 5. **5-secret-management-azure** - Azure Key Vault integration 15 | 6. **6-terraform-modules** - Module creation and usage 16 | 7. **7-terraform-azapi** - AzAPI provider usage 17 | 18 | ## Prerequisites and Installation 19 | 20 | ### Required Software 21 | - **Terraform**: HashiCorp Terraform CLI 22 | - **Azure CLI**: Version 2.0+ for Azure authentication 23 | - **Azure Subscription**: Active subscription for resource deployment 24 | 25 | ### Terraform Installation (TESTED - WORKING) 26 | **⚠️ TIMEOUT REQUIREMENT: Use 10+ minute timeout - NEVER CANCEL installation commands** 27 | 28 | For Ubuntu/Debian systems (recommended approach): 29 | ```bash 30 | # Add HashiCorp GPG key and repository (timeout: 300 seconds) 31 | wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg 32 | echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list 33 | 34 | # Update and install Terraform (timeout: 300 seconds) 35 | sudo apt update && sudo apt install terraform 36 | 37 | # Verify installation 38 | terraform -v 39 | ``` 40 | 41 | **Timing Measurements (VALIDATED):** 42 | - Repository addition: 15-30 seconds 43 | - Terraform installation: 2-5 minutes total 44 | - Version verification: < 1 second 45 | 46 | ### Azure CLI Verification 47 | ```bash 48 | # Check if Azure CLI is available 49 | az --version 50 | 51 | # If not installed, install using: 52 | curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash 53 | ``` 54 | 55 | ## Working with Repository Sections 56 | 57 | ### Section Navigation Pattern 58 | Each section follows this structure: 59 | ``` 60 | /X-section-name/ 61 | ├── README.md (start here) 62 | ├── 1-subsection/ 63 | ├── 2-subsection/ 64 | └── N-subsection/ 65 | ``` 66 | 67 | ### Basic Command Validation (TESTED - ALL WORKING) 68 | 69 | **⚠️ TIMEOUT REQUIREMENTS:** 70 | - `terraform init`: Use 120+ second timeout for initial runs 71 | - `terraform plan`: Use 300+ second timeout for Azure operations 72 | - `terraform apply`: Use 600+ second timeout - NEVER CANCEL 73 | - `terraform destroy`: Use 600+ second timeout - NEVER CANCEL 74 | 75 | #### Local Operations (No Azure Authentication Required) 76 | ```bash 77 | # Basic syntax validation (< 5 seconds each) 78 | terraform validate 79 | terraform fmt 80 | 81 | # Version check (< 1 second) 82 | terraform -v 83 | 84 | # Initialize providers (15-45 seconds typically) 85 | terraform init 86 | ``` 87 | 88 | #### Azure Operations (Require Authentication) 89 | ```bash 90 | # Login to Azure first 91 | az login 92 | 93 | # Plan deployment (1-5 minutes) 94 | terraform plan 95 | 96 | # Apply configuration (1-15 minutes depending on resources) 97 | terraform apply 98 | 99 | # Destroy resources (1-10 minutes) 100 | terraform destroy 101 | ``` 102 | 103 | ## Authentication Requirements 104 | 105 | ### When Azure Authentication is Needed 106 | - `terraform plan` with Azure provider 107 | - `terraform apply` with Azure resources 108 | - `terraform destroy` with Azure resources 109 | - Any operation that reads/writes Azure resources 110 | 111 | ### When Authentication is NOT Needed 112 | - `terraform validate` (syntax checking only) 113 | - `terraform fmt` (formatting only) 114 | - `terraform init` (downloads providers only) 115 | - `terraform -v` (version checking) 116 | 117 | ### Authentication Failure Patterns (< 1 second detection) 118 | ``` 119 | Error: building account: could not acquire access token 120 | Error: authentication failed 121 | Error: Please run 'az login' to setup account 122 | ``` 123 | 124 | **Solution**: Always run `az login` before Azure operations. 125 | 126 | ## Section-by-Section Guidance 127 | 128 | ### Section 1: Terraform Basics 129 | **Location**: `/1-terraform-basics/` 130 | **Prerequisites**: Terraform installed 131 | **Authentication**: Not required for basic commands 132 | 133 | **Navigation Path**: 134 | 1. Start with `/1-terraform-basics/README.md` 135 | 2. `/1-terraform-installation/` - Learn installation methods 136 | 3. `/2-terraform-commands/` - Practice basic commands 137 | 4. `/3-terraform-resources/` - Understand resource syntax 138 | 5. `/4-terraform-azure-provider/` - Azure provider configuration 139 | 140 | **Key Commands to Test**: 141 | ```bash 142 | cd 1-terraform-basics/2-terraform-commands 143 | terraform init 144 | terraform validate 145 | terraform fmt 146 | ``` 147 | 148 | ### Section 2: Terraform Variables 149 | **Location**: `/2-terraform-variables/` 150 | **Prerequisites**: Section 1 completed 151 | **Authentication**: May require Azure login for examples 152 | 153 | **Key Files**: 154 | - `1-terraform-variables.md` - Variable basics 155 | - `2-terraform-tfvars.md` - Using .tfvars files 156 | - `3-terraform-local-variables.md` - Local variable usage 157 | 158 | ### Section 3: Terraform State ⚠️ CRITICAL 159 | **Location**: `/3-terraform-state/` 160 | **Prerequisites**: Sections 1-2 completed 161 | **Authentication**: Required for state operations 162 | 163 | **⚠️ WARNING**: State management affects real Azure resources and costs. 164 | 165 | **Key Subdirectories**: 166 | - `/local-state-example/` - Local state demonstration 167 | - `/remote-state-example/` - Remote state with Azure Storage 168 | - `/scripts/` - Helper scripts 169 | 170 | **Critical Commands** (600+ second timeouts): 171 | ```bash 172 | cd 3-terraform-state/local-state-example 173 | terraform init 174 | terraform apply # Creates real Azure resources 175 | terraform destroy # ALWAYS run to avoid costs 176 | ``` 177 | 178 | ### Section 4: Terraform Advanced 179 | **Location**: `/4-terraform-advanced/` 180 | **Prerequisites**: Sections 1-3 completed 181 | **Authentication**: Required for examples 182 | 183 | **Subsections**: 184 | 1. `/1-depends-on/` - Resource dependencies 185 | 2. `/2-for-each/` - Iteration with for_each 186 | 3. `/3-count/` - Iteration with count 187 | 4. `/4-conditional-expressions/` - Conditional logic 188 | 5. `/5-dynamic-blocks/` - Dynamic configuration blocks 189 | 190 | ### Section 5: Secret Management Azure 191 | **Location**: `/5-secret-management-azure/` 192 | **Prerequisites**: Sections 1-4 completed 193 | **Authentication**: Required - uses Azure Key Vault 194 | 195 | **⚠️ SECURITY NOTE**: Handles sensitive data - ensure proper cleanup. 196 | 197 | ### Section 6: Terraform Modules 198 | **Location**: `/6-terraform-modules/` 199 | **Prerequisites**: Sections 1-5 completed 200 | **Authentication**: Required for module deployment 201 | 202 | **Focus**: Creating reusable Terraform modules. 203 | 204 | ### Section 7: Terraform AzAPI 205 | **Location**: `/7-terraform-azapi/` 206 | **Prerequisites**: All previous sections completed 207 | **Authentication**: Required - advanced Azure operations 208 | 209 | **Focus**: Using AzAPI provider for Azure resources not in AzureRM provider. 210 | 211 | ## Validation Scenarios 212 | 213 | ### Quick Repository Health Check 214 | ```bash 215 | # Verify repository structure (< 5 seconds) 216 | ls -la 217 | ls 1-terraform-basics/ 2-terraform-variables/ 3-terraform-state/ 218 | 219 | # Check Terraform installation (< 5 seconds) 220 | terraform -v 221 | 222 | # Test basic syntax validation in any section (< 10 seconds) 223 | cd 1-terraform-basics/2-terraform-commands 224 | terraform validate 225 | ``` 226 | 227 | ### Full Section Validation Workflow 228 | ```bash 229 | # Navigate to target section 230 | cd X-section-name/ 231 | 232 | # Read section README first 233 | cat README.md 234 | 235 | # Initialize if .tf files present (120+ second timeout) 236 | terraform init 237 | 238 | # Validate syntax (< 5 seconds) 239 | terraform validate 240 | 241 | # Format code (< 5 seconds) 242 | terraform fmt 243 | 244 | # For Azure operations - login first 245 | az login 246 | 247 | # Plan deployment (300+ second timeout) 248 | terraform plan 249 | 250 | # ONLY if comfortable with costs - apply (600+ second timeout) 251 | terraform apply 252 | 253 | # ALWAYS destroy afterwards (600+ second timeout) 254 | terraform destroy 255 | ``` 256 | 257 | ## Common Error Scenarios and Solutions 258 | 259 | ### 1. Authentication Errors 260 | **Symptom**: "authentication failed" or "could not acquire access token" 261 | **Solution**: 262 | ```bash 263 | az login 264 | az account show # Verify correct subscription 265 | ``` 266 | 267 | ### 2. Provider Download Issues 268 | **Symptom**: "terraform init" fails with network errors 269 | **Solution**: 270 | ```bash 271 | # Clear provider cache and retry 272 | rm -rf .terraform/ 273 | terraform init 274 | ``` 275 | 276 | ### 3. State Lock Issues 277 | **Symptom**: "state lock" errors 278 | **Solution**: 279 | ```bash 280 | # Force unlock (use carefully) 281 | terraform force-unlock 282 | ``` 283 | 284 | ### 4. Resource Already Exists 285 | **Symptom**: Resource conflicts during apply 286 | **Solution**: 287 | ```bash 288 | # Import existing resource or destroy and recreate 289 | terraform import . 290 | ``` 291 | 292 | ## Quick Reference Commands 293 | 294 | ### Directory Navigation 295 | ```bash 296 | # List all sections 297 | ls -d [1-7]-*/ 298 | 299 | # Navigate to specific section 300 | cd 1-terraform-basics/ 301 | cd 2-terraform-variables/ 302 | cd 3-terraform-state/ 303 | cd 4-terraform-advanced/ 304 | cd 5-secret-management-azure/ 305 | cd 6-terraform-modules/ 306 | cd 7-terraform-azapi/ 307 | 308 | # Find all Terraform files 309 | find . -name "*.tf" -type f 310 | ``` 311 | 312 | ### Essential Terraform Commands 313 | ```bash 314 | # Basic workflow (local operations) 315 | terraform validate && terraform fmt && terraform init 316 | 317 | # Azure workflow (requires authentication) 318 | az login && terraform plan && terraform apply 319 | 320 | # Cleanup workflow 321 | terraform destroy && rm -rf .terraform/ terraform.tfstate* 322 | ``` 323 | 324 | ### Azure CLI Quick Commands 325 | ```bash 326 | # Check authentication status 327 | az account show 328 | 329 | # List available subscriptions 330 | az account list 331 | 332 | # Switch subscription 333 | az account set --subscription "subscription-name-or-id" 334 | ``` 335 | 336 | ## Cost Management and Safety 337 | 338 | ### ⚠️ CRITICAL COST WARNINGS 339 | 1. **ALWAYS run `terraform destroy`** after testing each section 340 | 2. **Monitor Azure costs** - some resources incur charges immediately 341 | 3. **Use Azure Cost Management** to set spending alerts 342 | 4. **Verify destruction** using Azure Portal after destroy commands 343 | 5. **Delete Resource Groups** manually if Terraform destroy fails 344 | 345 | ### Resource Cleanup Checklist 346 | ```bash 347 | # 1. Terraform destroy 348 | terraform destroy 349 | 350 | # 2. Verify state is clean 351 | cat terraform.tfstate # Should be mostly empty 352 | 353 | # 3. Check Azure Portal 354 | az group list --output table 355 | 356 | # 4. Delete any remaining resources manually 357 | az group delete --name "resource-group-name" 358 | ``` 359 | 360 | ## Troubleshooting Quick Steps 361 | 362 | 1. **Always start with**: `terraform validate` 363 | 2. **For syntax errors**: `terraform fmt` 364 | 3. **For provider issues**: `rm -rf .terraform/ && terraform init` 365 | 4. **For auth issues**: `az login && az account show` 366 | 5. **For state issues**: Check `.terraform/` directory and `terraform.tfstate` 367 | 6. **For Azure errors**: Check Azure Portal for resource conflicts 368 | 369 | ## Important Notes 370 | 371 | - **Learning Path**: Follow sections 1→7 in order for best experience 372 | - **Time Investment**: Each section takes 30-60 minutes to complete properly 373 | - **Cost Awareness**: Sections 3+ create real Azure resources with potential costs 374 | - **Authentication**: Keep Azure CLI logged in for sections involving Azure resources 375 | - **Cleanup**: Always destroy resources after each section to avoid charges 376 | - **State Management**: Section 3 is critical - understand local vs remote state before proceeding 377 | 378 | This repository is designed for hands-on learning. Take time to understand each concept before moving to the next section. The progressive structure builds knowledge systematically from basic Terraform usage to advanced Azure integrations. --------------------------------------------------------------------------------