= Terraform in Azure Cloud Shell =
**Summary**: In this post I'll show you a few basic terraform commands and configuration files. If you're trying to learn terraform on azure this is an easy and fast tutorial to get started. \\
**Date**: 11 January 2025 \\
{{tag>terraform azure}}
We'll cover the following topics:
* Setup the main.tf file with the azurerm provider configuration and the resource group
* We will create terraform files for a vnet and a subnet
* We will create a vm with a ip address in the subnet
* We will have terraform create a random password for the vm
* We will figure out where to find the password
* At the end we will manually create an additional subnet and have the vm use it
In the end we will use {{{terraform destroy}}} to remove all resources.
== A Reminder on Terraform ==
Here is a list of terraform terminology and knowledge you need to know to follow this tutorial. Most of it was already covered [[terraform|here]], but I'll repeat it here for your convenience:
* Terraform looks automatically for all files with the .tf extension in the directory you run the command from and combines them into one configuration
* So you only need to define the provider once
* So you can use separate files for separate resources
* Hashicorp calls the terraform technical documentation online a "registry":
* https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs
* Make sure you know how to find the documentation for the resources you want to create:
* Use the 'Documentation' per provider for examples and authentication information
* If you google for 'terraform registry' + the resource you want to create you'll almost always find the right page
* For example: 'terraform registry azurerm virtual network'
* Use the version dropdown to find the version of the provider you are using:
terraform {
required_version = ">= 0.14.9"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "3.112"
}
}
}
* Resources describe infrastructure objects in Terraform configurations
* Data sources allow Terraform to use information defined outside of Terraform or defined by another separate Terraform configuration
== Start Cloud Shell ==
See [[https://learn.microsoft.com/en-us/azure/cloud-shell/get-started/classic?tabs=azurecli |here]] for how to start cloud shell. For direct access, use: https://shell.azure.com/
Once started, you can check if you're ready to go using the following commands:
terraform --version
cd clouddrive
code rg.tf
This willl check if terraform is available, and will start the integrated editor in the clouddrive directory.
== Setup the main.tf file ==
Use the code below to create a main.tf file in your cloud shell. Don't forget to change the subscription_id and tenant_id to your own values.
variable "resource_group_core" {
type = string
default = "rg_tf"
}
provider "azurerm" {
subscription_id = "30b3c71d-a123-a123-a123-abcd12345678"
tenant_id = "7e4an71d-a123-a123-a123-abcd12345678"
features {}
}
resource "azurerm_resource_group" "rg" {
name = var.resource_group_core
location = "West Europe"
}
Now, you can test the configuration by running the following commands:
terraform init
# output:
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/azurerm...
- Installing hashicorp/azurerm v4.15.0...
- Installed hashicorp/azurerm v4.15.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
# Now run the plan command
terraform plan
# output:
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# azurerm_resource_group.rg will be created
+ resource "azurerm_resource_group" "rg" {
+ id = (known after apply)
+ location = "westeurope"
+ name = "rg_tf"
}
Plan: 1 to add, 0 to change, 0 to destroy.
# Now run the apply command
terraform apply
# output:
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# azurerm_resource_group.rg will be created
+ resource "azurerm_resource_group" "rg" {
+ id = (known after apply)
+ location = "westeurope"
+ name = "rg_tf"
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
azurerm_resource_group.rg: Creating...
azurerm_resource_group.rg: Creation complete after 9s [id=/subscriptions/30b3c71d-a123-a123-a123-abcd12345678/resourceGroups/rg_tf]
== Create a VNet and Subnet ==
Now create a vnet.tf file with the following content:
resource "azurerm_virtual_network" "vnet" {
name = "vnet-sjoerd"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
address_space = ["10.0.0.0/16"]
}
resource "azurerm_subnet" "subnet" {
name = "snet-sjoerd"
virtual_network_name = azurerm_virtual_network.vnet.name
resource_group_name = azurerm_resource_group.rg.name
address_prefixes = ["10.0.1.0/24"]
}
You can now run {{{terraform apply}}} to create the vnet and subnet.
== Create a VM ==
Now create a vm.tf file with the following content. Note that the network interface is using the subnet we defined earlier. Because terraform is looking at all of the terraform files in the directory, it knows about the subnet:
resource "azurerm_network_interface" "nic" {
name = "nic-vm-sjoerd"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "Configuration"
subnet_id = azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_windows_virtual_machine" "vm" {
name = "vm-sjoerd"
admin_username = "azureuser"
admin_password = "password1234!"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
network_interface_ids = [azurerm_network_interface.nic.id]
size = "Standard_DS1_v2"
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2022-datacenter-azure-edition"
version = "latest"
}
}
You can now run {{{terraform apply}}} to create the vm and the network interface.
== Create a Random Password ==
As you can see above we have created a set password for the VM. You can't really use this in production environments as it exposes your password in plain text and possibly in a repository. So let's change the code to use a random password generated by terraform:
resource "random_password" "admin_password" {
length = 10
special = true
override_special = "!#$%&*()-_=+[]{}<>:?"
}
resource "azurerm_network_interface" "nic2" {
name = "nic-vm-sjoerd2"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "Configuration"
subnet_id = azurerm_subnet.subnet.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_windows_virtual_machine" "vm2" {
name = "vm-sjoerd2"
admin_username = "azureuser"
admin_password = random_password.admin_password.result
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
network_interface_ids = [azurerm_network_interface.nic2.id]
size = "Standard_DS1_v2"
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2022-datacenter-azure-edition"
version = "latest"
}
}
You can now run {{{terraform apply}}} to create the vm and the network interface. The new VM will have a random password.
=== Where to Find the Password ===
You can find the password in the terraform state file. You can find the state file in the .terraform directory in the directory you are running terraform from. The password is stored in plain text in the state file. You can run the following command to find the password: {{{code bash.tfstate}}}
Note that some characters in the password might be shown as a unicode character:
"password": "Oy\u003cQUJpwd%"
The above part {{{\u003c}}} is actually the {{{<}}} character. So be aware of {{{\uXXXX}}} characters in the password. The password above is actually: {{{Oy
data "azurerm_subnet" "subnet_manual" {
name = "snet-manual"
virtual_network_name = azurerm_virtual_network.vnet.name
resource_group_name = azurerm_resource_group.rg.name
}
resource "random_password" "admin_password3" {
length = 10
special = true
override_special = "!#$%*()-_=+[]{}:?"
}
resource "azurerm_network_interface" "nic3" {
name = "nic-vm-sjoerd3"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
ip_configuration {
name = "Configuration"
subnet_id = data.azurerm_subnet.subnet_manual.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_windows_virtual_machine" "vm3" {
name = "vm-sjoerd3"
admin_username = "azureuser"
admin_password = random_password.admin_password3.result
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
network_interface_ids = [azurerm_network_interface.nic3.id]
size = "Standard_DS1_v2"
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2022-datacenter-azure-edition"
version = "latest"
}
}
Now the subnet is defined as a data source, indicating it was created outside of terraform. You can now run {{{terraform apply}}} to create the vm and the network interface.
== Destroy All Resources ==
When you're done, you can run the following command to destroy all resources: {{{terraform destroy}}}