I have some servers managed and created via Terraform (in AWS). Other resources like ELB target groups, elastic IPs, etc are not managed by Terraform. So when I needed to change the server instance type, I manually created a new one in AWS, registered it in target groups and reassigned the IP.
And after that I wanted to import that newly created instance into Terraform and replace the old one in the state file. I removed the old one from the state using terraform state rm <resource_id>
and then called terraform import <resource_id> <new_instance_id>
and it worked fine, Terraform reported that everything has been imported.
But when after that I tried terraform apply
it showed me that the instance needs to be recreated. And when I answered "yes" it is indeed destroyed the manually created server and created a new one.
So how can I prevent this? Why import is useless? Changing of the instance type via Terraform is not an option, because it destroys the old instance first and then creates a new one, meaning that I'll have too big downtime.
When you import an existing object to a resource instance in Terraform, you must ensure that the object you're importing matches the current configuration as written in your Terraform module, because importing is the means to tell Terraform that it should take ownership of an existing object and thus change that object to match the desired state (described by the configuration) on future plan/apply rounds.
Exactly what to do here depends on exactly which attributes of the new object are disagreeing with the desired state to cause the provider to propose replacing the object. But you mentioned instance type, and so if the instance_type
attribute is the one that is forcing replacement then you have two main options:
Update the configuration for that resource to specify the same instance type as the object you've imported, so that the provider won't detect that the instance type is "wrong" and try to fix it by recreating the instance.
Add a lifecycle
block containing ignore_changes = [instance_type]
, which tells Terraform that it should not try to manage the instance_type
attribute except when creating a new instance for the first time.
Instead, the "refresh" phase during planning will discover the new instance_type
and record that as the current state, and the ignore_changes
option effectively means "the desired state is whatever the current state is", and so Terraform will hide the configured value from the provider to prevent it from planning a change.
An entirely different way to solve this problem, which some practitioners use, is to implement changes like this using Terraform but to make the change over two plan/apply rounds and two configuration changes:
Add a new autoscaling group with a different launch configuration while leaving the other one still configured.
Specify both of the autoscaling groups as targets for your load balancer so that when you plan and apply this first change the incoming requests will be spread across both the old and the new instances, since you will temporarily have twice as many servers as you would in your stable state.
Once you've applied that change and your monitoring suggests that the new servers are functioning correctly, remove the old autoscaling group from your configuration and remove the references to it in the load balancer configuration, and then plan and apply this change.
Terraform should then propose to destroy all of the old servers and remove their association with the autoscaling group, which is presumably okay because the new servers will still be running to continue serving requests without any downtime.
This kind of two-stage workflow is a Terraform-flavored variant of Blue-green deployment. Instead of leaving two sets of servers running constantly and switching between them, it starts up the second set of servers only when needed and then you can wait as long as you like, performing as many tests and checks as you like, before you destroy the old servers to complete the deployment and return to the stable state with only one set of servers.