I tried to get a better understanding of the Terraform State file that I used somehow, so I’ll put it on this post State purpose
Mapping to the real world
Terraform state files are used for the actual deployed infrastructure and its mapping. In the first version of Terraform, there was no state file, and it was managed by Tag, but depending on the cloud provider, there was no such thing, so it became a problem immediately.
terraform.tf
Let’s look at the state file generated for this config file. This is a resource that has already been destroyed, but specific data, such as an infrastructure identifier, has been generated. With this, Terraform knows which instance of the actual infrastructure corresponds to the defined infrastructure.
{
"version": 4,
"terraform_version": "0.12.18",
"serial": 5,
"lineage": "3822a78b-94a1-0b60-996a-aec909561c62",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "westus-rg",
"name": "test",
"provider": "provider.azurerm",
"instances": [
{
"schema_version": 0,
"attributes": {
"id": "/subscriptions/{MY_SUBSCRIPTION_ID}/resourceGroups/testResourceGroup1",
"location": "westus",
"name": "westus-rg",
"tags": {
"environment": "test branch"
}
},
"private": "{something}"
}
]
}
]
}
Metadata
There is another reason that terraform has a state file other than mapping. Holds metadata. Dependencies between resources are, for example. When deleting a resource, it is not possible to delete it properly unless its dependencies are known. It also includes the configuration of providers such as Azure and AWS.
As a sample, I added virtual network to the above tf file.
terraform.tf
Dependency is added, though not explicitly specified in the tf file.
{
"version": 4,
"terraform_version": "0.12.18",
"serial": 2,
"lineage": "cf4a560a-1759-63d1-7b4b-6ecae95fe7c3",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "azurerm_resource_group",
"name": "test",
"provider": "provider.azurerm",
"instances": [
{
"schema_version": 0,
"attributes": {
"id": "/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one",
"location": "westus",
"name": "testResourceGroup1-pr-branch-one",
"tags": {
"environment": "pr-branch-one branch"
}
},
"private": "{something}"
}
]
},
{
"mode": "managed",
"type": "azurerm_virtual_network",
"name": "test",
"provider": "provider.azurerm",
"instances": [
{
"schema_version": 0,
"attributes": {
"address_space": [
"10.0.0.0/16"
],
"ddos_protection_plan": [],
"dns_servers": null,
"id": "/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one/providers/Microsoft.Network/virtualNetworks/virtualNetwork1",
"location": "westus",
"name": "virtualNetwork1",
"resource_group_name": "testResourceGroup1-pr-branch-one",
"subnet": [],
"tags": {
"environment": "pr-branch-one branch"
}
},
"private": "{something}",
"dependencies": [
"azurerm_resource_group.test"
]
}
]
}
]
}
Performance
The state file holds a cache of resource attributes. terraform plan
Arrow terraform apply
when the, the default behavior in order to investigate the resources that are currently deployed, to make sure throwing a query against the API. When this becomes a big infrastructure, that query becomes a problem. Cloud providers often limit API requests per unit of time, and there is usually no API that answers all resources at once. This can be a problem for large infrastructure. In this case , use -refresh=false
a -target
flag. By the way, -target
the option to limit the resources to be deployed and the option -refresh
to update the state file from the current infrastructure true
will be the default .
Over the same period
By default, terraform stores state files in a local working directory, but when you work with a team, you will probably want to share state. At that time, you can use the remote state, for example, holding and sharing the state in a Storage Account.
Inspection and updating
State files are Json files, but updating them yourself is not recommended. It is recommended to use the terraform state subcommand.
For example, consider the case where the above virtual network part is moved to a module. Try refactoring the tf file. foo
And create the following two files.
foo/variables.tf
foo/virtualnet.tf
terraform.tf
terraform plan
Try refactoring in this way . Then the deployed VirtualNet is deleted and recreated, even though you don't want to change it.
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
azurerm_resource_group.test: Refreshing state... [id=/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one]
azurerm_virtual_network.test: Refreshing state... [id=/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one/providers/Microsoft.Network/virtualNetworks/virtualNetwork1]
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
- destroy
Terraform will perform the following actions:
# azurerm_virtual_network.test will be destroyed
- resource "azurerm_virtual_network" "test" {
- address_space = [
- "10.0.0.0/16",
] -> null
- dns_servers = [] -> null
- id = "/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one/providers/Microsoft.Network/virtualNetworks/virtualNetwork1" -> null
- location = "westus" -> null
- name = "virtualNetwork1" -> null
- resource_group_name = "testResourceGroup1-pr-branch-one" -> null
- tags = {
- "environment" = "pr-branch-one branch"
} -> null
}
# module.virtualnet.azurerm_virtual_network.test will be created
+ resource "azurerm_virtual_network" "test" {
+ address_space = [
+ "10.0.0.0/16",
]
+ id = (known after apply)
+ location = "westus"
+ name = "virtualNetwork1"
+ resource_group_name = "testResourceGroup1-pr-branch-one"
+ tags = {
+ "environment" = "pr-branch-one branch"
}
+ subnet {
+ address_prefix = (known after apply)
+ id = (known after apply)
+ name = (known after apply)
+ security_group = (known after apply)
}
}
Plan: 1 to add, 0 to change, 1 to destroy.
------------------------------------------------------------------------
Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
In such a case, change the state.
$ terraform state mv 'azurerm_virtual_network.test' 'module.virtualnet.azurerm_virtual_network.test'
Move "azurerm_virtual_network.test" to "module.virtualnet.azurerm_virtual_network.test"
Successfully moved 1 object(s).
Then, the resource moved to Module is recognized as the current resource. terraform plan
Now that you have changed the state, do one more time .
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
azurerm_resource_group.test: Refreshing state... [id=/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one]
module.virtualnet.azurerm_virtual_network.test: Refreshing state... [id=/subscriptions/{MY_SUBSCRIPTION}/resourceGroups/testResourceGroup1-pr-branch-one/providers/Microsoft.Network/virtualNetworks/virtualNetwork1]
------------------------------------------------------------------------
No changes. Infrastructure is up-to-date.
However, it now recognizes the same resource.
Format
The format of the state is JSON. State files are backward compatible. If you have a bug in terraform, you can easily update it with your own tools. There’s a version field that tells you if you want to go ahead when something changes.
I tried it at hand and when version
I changed it manually, it behaved as if the resource did not exist. In other words, it behaves like trying to create a separate tf file resource. version
If you undo, the resource appears to be up to date and seems to be back.