By going to any terraform provider / resource type, the format of the documentation is the same. This is great as this means training how to effecively read through a given provider / resource type can be reused.
If we take an Azure Virtual Machine as an example from the documentation -> azurerm_virtual_machine | Resources | hashicorp/azurerm | Terraform Registry
Notice the version of the given provider (In the top of the page) as it will become important later on. On the left side of the website you can see a hierarchy of the entire azurerm provider. What we are essentialy doing by using Google to search for the specific provider / resource type, is to let it fold out this hierarchy list and expand only the interesting elements.
Now, from the left side, notice how the Virtual Machine is under a section called "Compute" > "Resources" where "Compute" defines the Azure Resource Manager for the resource type "Compute" essentially Azure Virtual Machines, Azure Availability sets and so on. Below this lies the "Resources" Section. This is important as the "Resources" section will be present in ANY resource type as it basically stands for WRITE operations to set a provider.
On the same level as "Resources" the "Data Sources" section is defined which then stands for READING operations to set a provider. It shall also be noted that there will never be a guarantee of a provider having the exact same corresponding "Data Sources" resources as they are defined under "Resources". This greatly depend on the specific provider / resource type, in rare cases, even the provider version itself.
Going through the document on how to define an Azure VM from the documentation and from the top, see a detailed description below:
1. Example / Examples of how a resource can be defined (usually very lagcluster but will always be there)
2. Section "Argument Reference" which basically means that all possible attributes are set. There will always be shown the simple types first and then a list of possible "blocks" to define as a longer list on the bottom of the document.
3. Section "Attribute Reference" which basically means "What this provider can return you of data from the specific resource" (This will ALWAYS be at the bottom of the document for a resource)
4. When defining providers inside Terraform code, define STATIC versions that will match the latest providers page. This way you will NOT risk production breaking at any point in time even if the provider gets crucial updates.
We will define static providers in Terraform main.tf like so:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "=2.17.0" //Forcing the version with '='
}
}
}
Now, you might think, wait so this is all there is to it? You can only define provider versions using the equals operator or simply use the newest provider by ommiting the version parameter entirely. Well not exactly, as in some cases, we want to build modules to be reused in any new Terraform main.tf files the operator '>=' and this will be become a good advantage.
Lets say we have the main.tf file in our root and inside we have the same provider definitions as above. Now, inside we also have a folder called, kubernetes, which is its own main.tf file. This module is built to deploy, kubernetes, using the hashicorp provider, see the content below of the module main.tf file:
terraform {
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = ">=2.18.0"
}
}
}
Notice that we are using the '>=' symbol here which basically means 'must be a minimum version of 2.18.0' so we want our DevOps team to use this version of the provider or a newer one. This way we can build a more robust and a more error-free module, because end users importing it, will get an error if they plan to or already use a low version of Kubernetes.
Since all this is defined and saved, lets get back to our main.tf file in the root folder and expand it abit:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "=2.17.0" //Forcing the version with '='
}
}
}
module "kubernetes" {
source = "./kubernetes"
}
In this example, we have a main file that wants to use the Kubernetes module to deploy the resource type. To this the DevOps engineer normally works with the static version of '2.17.0' What happens when the person runs 'terraform init' ?
Yep... It fails, which it should
Make sure to add any kind of version contraints in the documentation for the module. Furthermore, when it comes to the versions inside of the Terraform scripts, either use a static version with '=' or simply ommit it. I highly recommend to use the static versions whenever it's possible, especially in production.
If we then changed the static version of '2.17.0' in our main file and instead set it to '2.18.0' The outcome will be successful.
Remember to delete the following 2 files before doing another init:
.terraform (folder) -> Terraform self-created
.terraform.lock.hcl -> Terraform self-created
This is to make Terraform forget that it already has created all its dependencies and what their versions are:
That is all for now, thank you - Remember to come back for another week of 'Terraform weekly tips' every Monday.
Want to learn more about Terraform -> terraform (codeterraform.com)
Want to learn more about other cool stuff like Automation or Powershell -> powershell (codeterraform.com) / automation (codeterraform.com)
Comments