Does Terraform intend to ignore changes if arguments are omitted? - terraform

Let's say I created an app service using azurerm_app_service with the bare minimum arguments. If I later go into Azure Portal and start creating IP restrictions and later run Terraform apply, it seems that Terraform doesn't detect drift and try to undo all of the IP restrictions. Is that working as intended? Is there Terraform documentation that says this is intended?
This behavior works in our favor, because it tells us that Terraform is OK with configuration drift so long as the argument or argument object is not defined in the configuration script. We intend to let Azure Portal users what is OK and not OK to modify in the portal without worrying about Terraform resetting it all.
Update: Accepted answer below and also received more feedback from Hashicorp's forum, https://discuss.hashicorp.com/t/terraform-not-detecting-drift-on-resources-intended-or-not/17547/4?u=terraform-man

It is going to depend on how the resource is implemented, the schema and what is changing. Typically with azurerm provider it should compute those changes even if they are optional. I would double check your terraform config and the portal resource you are modify are the same. You might need to run a refresh. In other words it is very common for changes in the portal to show up as drift when you run terraform.

This is called Desired State Vs Current State.
If you create any resource using terraform and manually modify it, Terraform reverts it after next apply.
Before Terraform Apply command, it fetches the current state into the statefile and then compares the current state vs desired state.
If there is any manual change, it will make necessary changes to make it as Desired state.

Related

Does terraform guarantee that if no changes were reported by plan, it will be able to recreate resources the same way they currently are?

I have a lot of resources in my Azure subscription. I want to manage them with terraform, so I want to import them using terraform import. I import every resource manually one-by-one. Then I run terraform plan and check that there are no changes to be made reported i.e. that the current infrastructure matches the configuration.
Does this mean that if I were to manually delete some of the resources via Azure portal or cli, I would be able to recreate them wit terraform apply perfectly so that they would have exactly the same configuration as before and would operate in exactly the same way?
In general Terraform cannot guarantee that destroying an object and recreating it will produce an exactly equivalent object.
It is possible for that to work, but it requires a number of things to be true, including:
Your configuration specifies the values for resource arguments exactly as they are in the remote API. For example, if a particular resource type has a case-insensitive (but case-preserving) name then a provider will typically ignore differences in case when planning changes but it will use exactly the case you wrote in the configuration, potentially selecting a different name.
The resource type does not include any "write-only" arguments. Some resource types have arguments that are used only by the provider itself and so they don't get saved as part of the object in the remote API even though they are saved in the Terraform state. terraform import therefore cannot re-populate those into the state, because there is nowhere else to read them from except the Terraform state.
The provider doesn't have any situations where it treats an omitted argument as "ignore the value in the remote system" instead of "unset the value in the remote system". Some providers make special exceptions for certain arguments where leaving them unset allows them to "drift" in the remote API without Terraform repairing them, but if you are using any resource types which behave in that way then the value stored in the remote system will be lost when you delete the remote object and Terraform won't be able to restore that value because it's not represented in your Terraform configuration.
The hashicorp/azurerm provider in particular has many examples of situation 3 in the above list. For example, if you have an azurerm_virtual_network resource which does not include any subnet blocks then the provider will not propose to delete any existing subnets, even though the configuration says that there should be no subnets. However, if you delete the virtual network and then ask Terraform to recreate it then the Terraform configuration has no record of what subnets were supposed to exist and so it will propose to create a network with no subnets at all.

When is terraform refresh used?

I'm currently learning terraform and I come across the command terraform refresh. It seems that it syncs the terraform.tfstate file to changes I did manually (I tried changing EC2 instance type). I found out that terraform plan can identify the drift between current and desired state without updating the tfstate file. Also, running terraform apply automatically updates the tfstate file.
So I was thinking, if there are any drifts detected during terraform plan, I will just update the terraform code to account for them and let terraform apply update the tfstate file. Is there any reason to use terraform refresh independently?
P.S.
I'm using terraform v0.15.0
You're correct that terraform refresh is used to update your terraform state file to match the present state--which can drift if resources have been edited outside of terraform.
terraform refresh itself is deprecated, with a note that it can be unsafe in certain situations. The documentation suggests using terraform apply -refresh-only as an alternative, since it prompts for the user to confirm the changes prior to them being persisted.
As to your question of "when is this used?". In my experience, which primarily uses terraform for AWS deployment, we almost never actually run a refresh operation. Terraform automatically checks current state as part of the terraform plan / terraform apply cycle. This may or may not be specific to the AWS provider.
The one scenario where I could see it being important to refresh the state is when the statefile is used as a datasource via a data remote_state_data block. Specifically, if you have intentionally modified the resource and cannot (or haven't yet) updated the terraform markup to reflect the change. In that scenario other terraform modules are reading values from your statefile (as opposed to from the resources themselves)--if your resource and statefile are out of sync then consumers of the statefile would receive inaccurate data.
However in most cases you want your resources to match their terraform representation--so you would terraform apply to bring the resources and state back in alignment with your terraform module.

How to import a remote resource while performing an apply in Terraform?

I'm using Terraform to create some resources. One of the side effects of creating the resource is the creation of another resource (let's call this B). The issue is that I can't access B to edit it in terraform because terraform considers it as "out of the state". I can't also import B in the state before the terraform apply is started because B does not exist.
Is there any solution to add (import) a remote resource to the state while running the apply command?
I'm thinking about this as a general question, if there was no solution I can also share the details of the resources I'm creating.
More details:
When I create a "Storage Account" on Azure using Terraform and enable static_website, Azure automatically creates a storage_container named $web. I need to edit one of the attributes of the $web container but Terraform tells me it is not in the current state and needs to be imported. Storage Account is A, Container is B
Unfortunately I do not have an answer to your specific question of importing a resource during an apply. The fundamental premise of Terraform is that it manages resources from creation. Therefore, you need to have a (in this case, azurerm_storage_container) resource declared, before you can import the current state of that resource into your state.
In an ideal world you would be able to explicitly create the container first and specify that the storage account uses that, but a quick look in the docs does not suggest that is an option (and I think is something you have already tried). If it is not exposed in Terraform, that is likely because it is not exposed by the Azure API (Disclaimer: not an Azure user)
The only (bad) answer I can think to suggest, is that you define an azurerm_storage_container data resource in your code, dependent on the the azurerm_storage_account resource, that will be able to pull back the details of the created container. You could then potentially have a null_resource that calls a local-exec provisioner that can fire a CLI command, using the params taken from the data resource to allow you to use the Azure CLI tools to edit the container.
I really hope someone else can come along with a better answer tho :|

How can one destroy terraform configuration in Azure efficiently?

I have an Azure terraform configuration. It sets up resource groups, key vaults, passwords, etc ...
When I destroy it terraform does the same in reverse - deleting secrets, access polices, key vaults and the last are resource groups.
But, if the resource groups are to be destroyed anyway, it makes sense just to destroy them first - all the child resources will be deleted automatically. But the azurerm provider does not do it this way.
What am I missing here? And if my understanding is correct, is there a way to implement it (without altering the provider, that is) ?
In Terraform's model, each resource is distinct. Although Terraform can see the dependencies you've defined or implied between them, it doesn't actually understand that e.g. a key vault is a child object of a resource group and so the key vault might be deleted as a side-effect of deleting the resource group.
With that said, unfortunately there is no built-in way in Terraform today to achieve the result you are looking for.
A manual approximation of the idea would be to use terraform state rm to tell Terraform to "forget" about each of the objects (that is, they will still exist in Azure but Terraform will have no record of them) that will eventually be destroyed as a side-effect of deleting the resource group anyway, and then running terraform destroy will only delete the resource group, because Terraform will believe that none of the other objects exist yet anyway. However, that is of course a very manual approach that, without some careful scripting, would likely take longer than just letting the Azure provider work through all of the objects in dependency order.
There is an exploratory issue in the Terraform repository that covers this use-case (disclaimer: I'm the author of that issue), but the Terraform team isn't actively working on that at the time I write this, because efforts are focused elsewhere. The current set of use-cases captured there doesn't quite capture your idea here of having Terraform recognize when it can skip certain destroy operations, so you might choose to share some details about your use-case on that issue to help inform potential future design efforts.
Terraform is built this way, it wouldn't traverse the graph and understand that if the resource group is deleted - anything inside resource group will be deleted as well. which isn't even true in some cases. So I would say it doesn't make sense to do that.
Only real time when this is annoying - when you are testing. for that time you can create a script that would initiate resource group deletion and clear local state, for example

Does terraform apply also refresh the state against what is already deployed?

Suppose I run terraform apply without the refresh flag and with no plan mentioned.
Will terraform refresh the state of what I have defined as IaC against the state of what is already deployed? Or will it only look at my current state file and compare that with what I updated as IaC.
The documentation does not mention the default refresh behavior of terraform.
Terraform will automatically refresh the state before running a command that would rely on it (such as plan, apply, destroy).
You can see this yourself by looking at the output from running these commands:
$ 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.
data.aws_region.current: Refreshing state...
------------------------------------------------------------------------
No changes. Infrastructure is up-to-date.
This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.
terraform apply will check for new state always. Any terraform action will always first refresh the state by default. Ideally, you should always do a terraform plan -out <planName>.plan and then apply this plan on the environment using terraform apply <planName>.plan. This ensures consistency and you exactly know what is going to happen in the environment.
In cases where the plan is old and there have been changes to the environment meanwhile, terraform will detect that and will give you an error - forcing you to plan and apply again.

Resources