Terraform: Migrate from template_file Data Sources
Overview
I stumbled across this problem when using a new MacBook Pro M1 CPU. I had some legacy code that used aws_template for a user data script. The template function is now deprecated, and the only way around this is to use the templatefile
a function that was introduced in Terraform 0.12.
This is simple for the new code, but if, like me, you have legacy code that is still embedded into production, you will have issues.
This is the error I was getting:
template v2.2.0 does not have a package available for your current platform, darwin_arm64
There are many suggested fixes online, but they just fix the issue locally. It doesn’t fix the issue in the code and the terraform state files (mine are located in S3)
Migrating
Locate current usage
To begin, you can check to see what requires this provider in your current code – in the below example, you can see that the template provider is required by both resources in module.jenkins
and the state:
Please note that I use AWS-Vault to access my cloud resources.
aws-vault exec <my-aws-profile> --no-session -- terraform providers
Providers required by configuration: .
โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ provider[registry.terraform.io/opsgenie/opsgenie] 0.6.10
โโโ provider[terraform.io/builtin/terraform]
โโโ module.jenkins
โโโ provider[registry.terraform.io/hashicorp/random]
โโโ provider[registry.terraform.io/hashicorp/template]
โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.codebuild_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.iam_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.s3_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.security_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.slaves_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.network_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.ec2_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.lb_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.log_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.api_integration_autoscaling
โ โโโ provider[registry.terraform.io/opsgenie/opsgenie] 0.6.10
โโโ module.jenkins_alert_poli
โ โโโ provider[registry.terraform.io/opsgenie/opsgenie] 0.6.10
โโโ module.certs_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.api_integration_cloudwatch #
โโโ provider[registry.terraform.io/opsgenie/opsgenie]
0.6.10 Providers required by state: provider[registry.terraform.io/opsgenie/opsgenie] provider[registry.terraform.io/hashicorp/aws] provider[registry.terraform.io/hashicorp/random] provider[registry.terraform.io/hashicorp/template] provider[terraform.io/builtin/terraform]
Removing Current usage
Search for all instances of the template_file
data resource and replace it with a template file function – for example, this CloudWatch dashboard changes
From This:
data "template_file" "dashboard" {
template = file("${path.module}/templates/dashboard.template")
resource "aws_cloudwatch_dashboard" "jenkins" {
dashboard_name = "Jenkins" dashboard_body = data.template_file.dashboard.rendered
}
}
To This:
resource "aws_cloudwatch_dashboard" "jenkins" {
dashboard_name = "Jenkins"
dashboard_body = templatefile("${path.module}/templates/dashboard.template", {})
}
Full details of how to use the function, and pass variables, can be found here
Now, if you check the providers again, you should see that the modules are no longer listing template
as a required provider – however, the state does:
aws-vault exec <my-aws-profile> --no-session -- terraform providers
Providers required by configuration: .
โโโ provider[registry.terraform.io/opsgenie/opsgenie] 0.6.10
โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ provider[terraform.io/builtin/terraform]
โโโ module.jenkins
โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ provider[registry.terraform.io/hashicorp/random]
โโโ module.network_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.certs_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.codebuild_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.ec2_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.iam_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.api_integration_autoscaling
โ โโโ provider[registry.terraform.io/opsgenie/opsgenie] 0.6.10
โโโ module.api_integration_cloudwatch
โ โโโ provider[registry.terraform.io/opsgenie/opsgenie] 0.6.10
โโโ module.slaves_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.jenkins_alert_policy
โ โโโ provider[registry.terraform.io/opsgenie/opsgenie] 0.6.10
โโโ module.s3_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.security_label
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.log_label #
โ โโโ provider[registry.terraform.io/hashicorp/aws]
โโโ module.lb_label
โโโ provider[registry.terraform.io/hashicorp/aws]
Providers required by state: provider[registry.terraform.io/opsgenie/opsgenie]
provider[registry.terraform.io/hashicorp/aws] provider[registry.terraform.io/hashicorp/random] provider[registry.terraform.io/hashicorp/template] provider[terraform.io/builtin/terraform]
Update State
Apply the Terraform and (assuming you got your code correct!) you should see that there were no changes to apply – this is because the template should be the same, just the method of generating it has changed:
aws-vault exec <my-aws-profile> --no-session -- terraform apply ... No changes.
Your infrastructure matches the configuration. Your configuration already matches the changes detected above. If you'd like to update the Terraform state to match, create and apply a refresh-only plan: terraform apply -refresh-only Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
After you successfully apply, you should refresh the state:
aws-vault exec <my-aws-profile> --no-session -- terraform apply -refresh-only .... No changes. Your infrastructure still matches the configuration. Terraform has checked that the real remote objects still match the result of your most recent changes, and found no differences. Would you like to update the Terraform state to reflect these detected changes? Terraform will write these changes to the state without modifying any real infrastructure. There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Now when you check, you will see that the provider is no longer needed by the state either:
Providers required by state: provider[terraform.io/builtin/terraform] provider[registry.terraform.io/opsgenie/opsgenie] provider[registry.terraform.io/hashicorp/aws] provider[registry.terraform.io/hashicorp/random]
At this point, you can remove the .terraform
directory and re-init the code (terraform init -reconfigure
) – all the modules and providers (now excluding the template provider) will be installed and the .terraform.lock.hcl
will have its entry for the template
provider removed.
At this point, you can commit your code back to git.
How about providing some complete examples? Where are the closing brackets for your example functions and data resources?
Thanks for the feedback. I have fixed my examples.