OCI FortiGate HA Cluster – Reference Architecture: Code Review and Fixes
Introduction OCI Quick Start repositories on GitHub are collections of Terraform scripts and configurations provided by Oracle. These repositories ... Read More
Learn more about why Eclipsys has been named the 2023 Best Workplaces in Technology and Ontario, Certified as a Great Place to Work in Canada and named Canada’s Top SME Employer!
Learn more!In this tutorial, we will try to provision a VM (Virtual Machine) on Azure using their Terraform provider and reproduce the same deployment I have completed on AWS (Amazon Web Services) and Oracle Cloud Infrastructure (OCI). As usual we won’t just deploy an instance but also configure an nginx website linked to its public IP. I’ll end this post with some notes on Microsoft Azure and Amazon Web Services differences.
I used azurerm_linux_virtual_machine resource instead of the classic vm resource because azure decided it was better to split both Windows/Linux for VM deployments (provider 2.0) which is unlike anything seen elsewhere.
Note: I have done the same task with my AZ-CLI based bash scripts (avaialble in GitHub) which was very helpful to understand the logic behind components needed during the VM creation (More details: deploy-webserver-vm-using-azure-cli).
Here’s a direct link to my GitHub repo linked to this lab: terraform-examples/terraform-provider-azure
Content:
I. Terraform Setup
IV. Partial Deployment
V. Full Deployment
Tips and Conclusion
The following illustration shows the layers involved between your workstation an Oracle Cloud Infrastructure while running the Terraform actions along with the instance attributes we will be provisioning.
Besides describing my GitHub repo before starting this tutorial, I’ll just briefly discuss some principles.
Infrastructure As Code manages and provisions cloud resources using a declarative code (i.e Terraform) and definition files avoiding interactive configuration. Terraform is an immutable Orchestrator that creates and deletes all resources in the proper sequence. Each Cloud vendor has what we call a provider that terraform uses in order to convert declarative texts into API calls reaching the Cloud infrastructure layer.
Terraform Files
– Can be a single file or split into multiple tf or tf.json files, any other file extension is ignored.
– Files are merged in alphabetical order but resource definition order doesn’t matter (subfolders are not read).
– Common configurations have 3 type of tf files and a statefile.
Terraform resource declaration syntax looks like this:
Component "Provider_Resource_type" "MyResource_Name" { Attribute1 = value ..
Attribute2 = value ..}
Where do I find a good Azure deployment sample?
The easiest way is to create/locate an instance from the console and then use the import function from terraform to generate each of the related components in HCL format (vnet, instance,subnet,etc..) based on their id.
Example for a Vnet >>
1- # vi vnet.tf
provider "azurerm" {
features {}
}
resource "azurerm_virtual_network" "terra_vnet" {
}
2- # terraform import azurerm_virtual_network.terra_vnet /subscriptions/00*/resourceGroups/Mygroup/providers/Microsoft.Network/virtualNetworks/terra_vnet
3- # terraform show -no-color > vnet.tf
Note:
If you want to import all the existing resources in your account in bulk mode (not one by one) there are tools like py-az2tf or terraformer, which can import both code and state from your azure account automatically.
Terraform lab content: I have deliberately split this lab in 2:
Although I’m on windows I tried the lab using WSL (Ubuntu) terminal (but same applies to Mac).
Windows: Download and run the installer from their website (32-bit ,64-bit)
Linux: Download, unzip and move the binary to the local bin directory
$ wget https://releases.hashicorp.com/terraform/1.0.3/terraform_1.0.3_linux_amd64.zip
$ unzip terraform_1.0.3_linux_amd64.zip
$ mv terraform /usr/local/bin/
$ terraform --version
Terraform v1.0.3
Microsoft Azure authentication
To authenticate with your azure account, Terraform will only need you to login using az cli . This can be done by running az cli commands described here >> az-cli installation
Assumptions
– I will assume that below authentication option is present in your workstation:
$ az account show
EnvironmentName HomeTenantId IsDefault Name State
----------------- ---------------- ----------- ------------- -------
AzureCloud 00000000-00000000… True BrokeDba Lab Enabled
– I’ll also assume the presence of an ssh key pair to attach to your vm instance. If not here is a command to generate a PEM based key pair.
$ ssh-keygen -P "" -t rsa -b 2048 -m pem -f ~/id_rsa_az
Generating public/private rsa key pair.
Your identification has been saved in /home/brokedba/id_rsa_az.
Your public key has been saved in /home/brokedba/id_rsa_az.pub.
$ git clone https://github.com/brokedba/terraform-examples.git
Note: As explained earlier you will find 2 directories inside the repository which will make things easier:
terraform-provider-azure/create-vnet/
To grasp how we deploy a single Vnet.terraform-provider-azure/launch-instance/
For the final instance deploy.
terraform-provider-azure/create-vnet”
where our configuration resides (i.e vnet)$ cd /mnt/c/Users/brokedba/azure/terraform-examples/terraform-provider-azure/create-vnet
”terraform init”
.$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding latest version of hashicorp/azurerm...
- Installing hashicorp/azurerm v2.80.0...
* Installed hashicorp/azurerm v2.80.0 (signed by HashiCorp)
provider "azurerm" {
Terraform has been successfully initialized!
$ terraform --version
Terraform v1.0.3 on linux_amd64
+ provider registry.terraform.io/hashicorp/azurerm v2.80.0
”create-vnet”
directory. Here, only *.tf
files matter (click to see content)$ tree
.
|-- outputs.tf ---> displays resources detail after the deploy
|-- variables.tf ---> Resource variables needed for the deploy
|-- vnet.tf ---> Our vnet terraform declaration
terraform plan
command to create an execution plan (quick dry run to check the desired state/actions). Specify the prefix for your resource names$ terraform plan
var.prefix The prefix used for all resources in this example
Enter a value: Demo
Terraform used selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols: + create
------------------------------------------------------------------------
Terraform will perform the following actions:
# azurerm_network_security_group.terra_nsg will be created
+ resource "azurerm_network_security_group" "terra_nsg"
{..
+ security_rule = [
+ destination_port_ranges = [+ "22", + "3389",+ "443",+ "80",]
+ direction = "Inbound"
+ name = "Inbound HTTP access"
# azurerm_resource_group.rg will be created
+ resource "azurerm_resource_group" "rg"
{..}
# azurerm_subnet.terra_sub will be created
+ resource "azurerm_subnet" "terra_sub"
{..
+ address_prefixes = ["192.168.0.0/16” ]
…}
# azurerm_subnet_network_security_group_association.nsg_sub will be created
+ + resource "azurerm_subnet_network_security_group_association" "nsg_sub"{
# azurerm_virtual_network.terra_vnet will be created
+ resource "azurerm_virtual_network" "terra_vnet" {
...
+ address_space = [ "192.168.0.0/16”
...}
Plan: 5 to add, 0 to change, 0 to destroy.
– The output being too verbose I deliberately kept only relevant attributes for the Vnet resource plan
”terraform deploy”
to provision the resources to create our Vnet (listed in the plan)$ terraform apply -auto-approve
azurerm_resource_group.rg: Creating...
azurerm_virtual_network.terra_vnet: Creating...
azurerm_network_security_group.terra_nsg: Creating...
azurerm_subnet.terra_sub: Creating...
...
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
Outputs:
Subnet_CIDR = 192.168.10.0/24
Subnet_Name = internal
vnet_CIDR = 192.168.0.0/16
vnet_Name = Terravnet
vnet_dedicated_security_group_Name = "Demo-nsg"
vnet_CIDR = 192.168.0.0/16
vnet_dedicated_security_ingress_rules = toset([
{…
"access" = "Allow"
"description" = "RDP-HTTP-HTTPS ingress trafic"
"destination_port_ranges" = toset([
"22",
"3389",
"443",
"80",])
…
Observations:
– The deploy started by loading the resources variables in variables.tf which allowed the execution of vnet.tf
– Finally terraform fetched the attributes of the created resources listed in outputs.tf
Note: We’ll now destroy the Vnet as the next instance deploy contains the same Vnet specs.
$ terraform destroy -auto-approve
Destroy complete! Resources: 6 destroyed.
terraform-provider-azure/launch-instance/
$ tree ./terraform-provider-azure/launch-instance
.
|-- cloud-init --> SubFolder
| `--> centos_userdata.txt --> script to config a webserver+ modify the index
| `--> sles_userdata.txt --> for SUSE
| `--> ubto_userdata.txt --> for Ubunto
| `--> el_userdata.txt --> for Enteprise linux distros
| `--> Win_userdata.ps1 --> for windows
|-- compute.tf ---> Instance related terraform configuration
|-- outputs.tf ---> displays the resources detail at the end of the deploy
|-- variables.tf ---> Resource variables needed for the deploy
|-- vnet.tf ---> same vnet we deployed earlier
Note: As you can see we have 2 additional files and one Subfolder. compute.tf is where the compute instance and all its attributes are declared. All the other “.tf” files come from my vnet example with some additions for variables.tf and output.tf
...variable "user_data" { default = "./cloud-init/centos_userdata.txt"}
$ vi compute.tf
resource "azurerm_linux_virtual_machine" "terravm" {
...
custom_data = base64encode ("${file(var.user_data)}")
...
$ vi compute.tf resource "azurerm_linux_virtual_machine" "terravm" { .. admin_ssh_key { username = var.os_publisher[var.OS].admin public_key = file("~/id_rsa_az.pub") } ## Change me
launch-instance”
directory, you can run the plan command to validate the 9 resources required to launch our vm instance. The output has been truncated to reduce verbosity$ terraform plan
Terraform used selected providers to generate following execution plan.
Resource actions are indicated with the following symbols:
------------------------------------------------------------------------
Terraform will perform the following actions:
... # Vnet declaration (see previous vnet deploy)
...
# azurerm_linux_virtual_machine.terravm will be created
+ resource "azurerm_linux_virtual_machine" "terravm" {
+ ...
+ admin_username = "centos"
+ location = "eastus"
+ size = "Standard_B1s"
+ name = "TerraDemo-vm"
+ private_ip = "192.168.10.51"
+ admin_ssh_key { = {
+ public_key = <<-EOT ssh-rsa AAAABxxx…*
EOT
username =”centos” }
+ custom_data = (sensitive value)
+ os_disk {…}
+ source_image_reference {
+ offer = "CentOS"
+ publisher = "OpenLogic"
+ version = "latest"
}
}
# azurerm_network_interface.Terranic will be created
# azurerm_network_interface_security_group_association.terra_assoc_pubip_nsg will be created
# azurerm_network_security_group.terra_nsg will be created
# azurerm_public_ip.terrapubip will be created
...}
...
}
Plan: 9 to add, 0 to change, 0 to destroy.
$ terraform apply -auto-approve
...
azurerm_resource_group.rg: Creating...
azurerm_virtual_network.terra_vnet: Creating...
azurerm_network_security_group.terra_nsg: Creating...
azurerm_subnet.terra_sub: Creating...
azurerm_subnet_network_security_group_association.nsg_sub: Creating...
azurerm_network_interface.Terranic: Creating...
azurerm_linux_virtual_machine.terravm: Creating......
Apply complete! Resources: 9 added, 0 changed, 0 destroyed.
Outputs:
...
Subnet_Name = "internal"
vnet_CIDR = 192.168.0.0/16
Subnet_CIDR = 192.168.10.0/24
vnet_dedicated_security_ingress_rules = toset([
{…
"access" = "Allow"
"description" = "RDP-HTTP-HTTPS ingress trafic"
"destination_port_ranges" = toset([
"22",
"3389",
"443",
"80",])
]
SSH_Connection = ssh connection to instance TerraCompute ==> sudo ssh -i ~/id_rsa_az centos@’’ <---- IP not displayed due to bug with az provider
private_ip = "192.168.10.4"
public_ip = ""
$ terraform output SSH_Connection
ssh connection to instance TerraCompute ==> sudo ssh -i ~/id_rsa_az centos@ ’’
$ SUBNET ID /subscriptions/xx/resourceGroups/my_group/providers/Microsoft.Network/virtualNetworks/MY-VNET/subnets/My_SUBNET
variables.tf
file.Introduction OCI Quick Start repositories on GitHub are collections of Terraform scripts and configurations provided by Oracle. These repositories ... Read More
Introduction So far, I have used Oracle AutoUpgrade, many times in 3 different OS’. Yet the more you think you’ve seen it all and reached the ... Read More