Terraform provider upgrades
HashiCorp Products | HCP Terraform, Terraform Enterprise, Terraform Community Edition |
---|---|
Partner Products | N/A |
Maturity Model | Standardize |
Use Case Coverage | Managing Terraform provider upgrades |
Tags | Terraform |
Guide Type | HVD Integration Guide |
Publish Date, Version | August/2/2024, Version 1.0.1 |
Authors | Craig Sloggett |
Purpose of this guide
This guide provides a step-by-step workflow for upgrading Terraform provider versions, applicable to both HashiCorp Cloud Platform (HCP) and the Community Edition.
Upgrading a provider often presents challenges, such as maintaining infrastructure integrity and addressing unforeseen bugs. This workflow ensures a smooth and confident upgrade process by walking you through a detailed example involving an AWS provider version upgrade.
Target audience
- Platform teams
- Producers and consumers of the platform
The workflow outlined in this document is useful to anyone required to deploy infrastructure in a safe and predictable way. Even with an automated pipeline, there will be circumstances where you have to refactor your code to support provider changes.
Benefits
Upgrading the Terraform provider you're using can provide several significant benefits:
- Enable support for new cloud provider features.
- Ensure infrastructure security with the latest patches.
- Improve deployment performance with optimizations.
- Resolve bugs and issues present in older provider versions.
- Benefit from the latest community knowledge, documentation, and support resources.
- Prevent the accumulation of technical debt with regular upgrades.
Understanding this workflow will equip you with the skills needed to handle any scenario during the provider upgrade process.
Prerequisites
This pattern assumes you understand the core Terraform workflow(opens in new tab) and use git for version control(opens in new tab) in a pull(opens in new tab)/merge(opens in new tab) request workflow.
The term platform used throughout this document refers to a standardized set of tools, processes, and services to support application development and deployment across various environments.
Before starting this guide, please review the following resources:
- Build in AWS with Terraform(opens in new tab)
- Git Tutorial(opens in new tab)
- Configuring Notifications in GitHub(opens in new tab)
- How to Read a git diff(opens in new tab)
- Speculative Plans(opens in new tab)
- What Is a Platform Team and What Problems Do They Solve?(opens in new tab)
Integration architecture
The main components of this workflow are:
- Monitoring for a new provider version.
- Updating Terraform configuration to use the new provider version.
- Verifying there are no changes using the speculative plan output.
- Refactoring Terraform configuration.
- Verifying your deployment after upgrading.
Summary
When upgrading the Terraform provider in your code base, you may have to make changes to your configuration, depending on the output of a speculative plan(opens in new tab). This guide outlines both the overall process to upgrade a Terraform provider, and a detailed refactoring workflow.
The following is an overview of the upgrade process:
The following is an overview of the refactoring process:
Best practices
- Upgrade providers often, ideally as they are published.
- Isolate provider upgrades into their own separate
git
branch and pull request. - When tackling issues during an upgrade, refactor for one issue at a time.
- Continuously verify (small) changes by running a speculative plan.
- Only run
terraform apply
once there are no errors, warnings, or actions in a speculative plan. - Ensure the only change is the provider version configuration when assessing the initial speculative plan output.
Document context
People and process
The Terraform: Operating Guide for Adoption(opens in new tab) has extensive discussion on the people and process recommendations for platform operations.
Anyone required to deploy infrastructure in a safe and predictable way will benefit from this guide. With that in mind, there is a slight difference in responsibilities between the platform team and those consuming the platform.
At a high level, the platform team is responsible for monitoring and publishing new versions, but it’s up to the consumers to implement change.
Tip
Consumers of the platform often include the platform team itself.Platform team
The platform team is responsible for:
- Staying informed about new releases of the Terraform providers in use on the platform.
- Testing new versions in a staging environment before deployment to production.
- Ensuring all relevant documentation is updated to reflect changes in the new versions.
- Communicating upcoming upgrades and potential impacts to all stakeholders.
- Providing training and support to ensure smooth adoption of new versions.
Producers/consumers of the platform
Producers and consumers of the platform are responsible for:
- Staying informed about new versions available for the Terraform providers used to deploy their application infrastructure.
- Testing their own configurations and modules with the new provider versions in a staging environment.
- Providing feedback to the platform team about any issues encountered during testing.
- Updating their own documentation and workflows to align with the new versions.
- Attending training sessions and actively engaging with support resources provided by the platform team.
Object planning considerations
When using either HCP Terraform or Terraform Enterprise, infrastructure resources are organized into organizations, projects, and workspaces, with access managed through user accounts, teams, and permissions. Planning how to organize these objects are outlined in the Configuring for First Use(opens in new tab) section of the Terraform: Operating Guide for Adoption in detail.
Checklist
- Terraform has been installed and configured on your machine with the ability to run a speculative plan against the code base being upgraded.
- Push access to a
git
repository containing Terraform configuration files. - A GitHub account to receive notifications for new provider versions.
- Access to deploy an S3 bucket to AWS using their API.
Workflow
The following workflow uses the example of upgrading the AWS provider from v3.74.3
to v3.75.0
where configuration within the aws_s3_bucket
resource is deprecated and moved to a separate resource. The new resource is then added to the configuration to ensure the end state is the same in AWS.
There will also be an “unexpected” change introduced that showcases the discovery of a bug in the AWS provider.
Step 1: Deploy stable configuration
Task: Deploy stable configuration to AWS.
Persona: Producers and consumers of the platform.
Description: To begin, we are going to go back in time to v3.74.3
of the AWS provider and create an S3 bucket with versioning enabled using the versioning
configuration block:
terraform {
required_version = "~> 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "3.74.3"
}
}
}
provider "aws" {}
resource "aws_s3_bucket" "testing" {
bucket = “testing-provider-upgrades-bucket-00001”
versioning {
enabled = true
}
# Matching the SSE defaults to eliminate diffs in the plan output.
server_side_encryption_configuration {
rule {
bucket_key_enabled = false
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
}
As expected, there are no errors or warnings when deploying our infrastructure to AWS:
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "3.74.3"...
- Installing hashicorp/aws v3.74.3...
- Installed hashicorp/aws v3.74.3 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
$ terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_s3_bucket.testing will be created
+ resource "aws_s3_bucket" "testing" {
+ acceleration_status = (known after apply)
+ acl = "private"
+ arn = (known after apply)
+ bucket = "testing-provider-upgrades-bucket-00001"
+ bucket_domain_name = (known after apply)
+ bucket_regional_domain_name = (known after apply)
+ force_destroy = false
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ region = (known after apply)
+ request_payer = (known after apply)
+ tags_all = (known after apply)
+ website_domain = (known after apply)
+ website_endpoint = (known after apply)
+ server_side_encryption_configuration {
+ rule {
+ bucket_key_enabled = false
+ apply_server_side_encryption_by_default {
+ sse_algorithm = "AES256"
}
}
}
+ versioning {
+ enabled = true
+ mfa_delete = false
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_s3_bucket.testing: Creating...
aws_s3_bucket.testing: Creation complete after 2s [id=testing-provider-upgrades-bucket-00001]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Step 2: Monitor for new provider versions
Task: Setup notifications for new provider versions.
Persona: Producers and consumers of the platform.
Description: To effectively monitor for new releases of a Terraform provider, using an automation tool like Renovate(opens in new tab) or GitHub’s Dependabot(opens in new tab) would be ideal. These tools will automatically generate a pull request against your project to upgrade the version of dependencies in use (like a Terraform provider).
Alternatively, developers can subscribe to provider release notifications(opens in new tab) by selecting Releases under the Watch menu of the upstream GitHub repository:
Now, when a new release is published, you will be notified by email (and in your GitHub notifications inbox).
Step 3: Update provider configuration
Task: Update the version argument and install the new provider version.
Persona: Producers and consumers of the platform.
Description: Once you have been notified about a new provider version, the next step is to update the provider configuration to include the latest version. This involves modifying the version
argument for the aws
block within the required_providers
block.
Ensure you work on a new git
branch to isolate changes:
$ git branch upgrade-aws-provider
$ git push -u origin upgrade-aws-provider
$ git checkout upgrade-aws-provider
Update the version argument for the AWS provider:
diff --git a/versions.tf b/versions.tf
index 4800017..d9536c5 100644
--- a/versions.tf
+++ b/versions.tf
@@ -4,7 +4,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = "3.74.3"
+ version = "3.75.0"
}
}
}
Note
It is critical that the provider version is the only change made to the Terraform configuration when assessing the speculative plan outputs. Introducing the least amount of change possible ensures that any detected modifications are solely due to the provider update, enabling a clear and accurate evaluation of the impact.Install the new provider version as configured:
$ terraform init -upgrade # This ensures Terraform downloads the provider version specified.
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "3.75.0"...
- Installing hashicorp/aws v3.75.0...
- Installed hashicorp/aws v3.75.0 (signed by HashiCorp)
Terraform has made some changes to the provider dependency selections recorded
in the .terraform.lock.hcl file. Review those changes and commit them to your
version control system if they represent changes you intended to make.
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Step 4: Run speculative plan
Task: Execute a speculative plan to test changes with the updated provider version.
Persona: Producers and consumers of the platform.
Description: After updating the provider version in the Terraform configuration, the next step is to run a speculative plan. This step allows you to preview the potential changes without actually applying them, ensuring that the new provider version does not introduce unexpected modifications to the infrastructure.
Tip
The ultimate goal is to have no errors, warnings, or actions when upgrading to a newer provider version.If you follow the advice found in the Terraform Workflows(opens in new tab) section of the Terraform: Operating Guide for Adoption and configured a VCS driven workflow, you could open a pull request right now and git push
your changes to run a speculative plan.
However, since this workflow guides you (a single developer) through a provider upgrade, by rapidly iterating through the core Terraform workflow, we will use the CLI to run speculative plans.
Note
If you runterraform plan
without the -out=FILE
option then it will create a speculative plan, which is a description of the effect of the plan but without any intent to actually apply it.Run a speculative plan:
$ terraform plan
aws_s3_bucket.testing: Refreshing state... [id=testing-provider-upgrades-bucket-00001]
No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.
╷
│ Warning: Argument is deprecated
│
│ with aws_s3_bucket.testing,
│ on main.tf line 1, in resource "aws_s3_bucket" "testing":
│ 1: resource "aws_s3_bucket" "testing" {
│
│ Use the aws_s3_bucket_versioning resource instead
│
│ (and 3 more similar warnings elsewhere)
╵
Our first warning after updating the provider version is for the use of a deprecated argument.
Step 5: Identify errors, warnings, or actions
Task: Identify any errors, warnings, or actions as indicated by a speculative plan.
Persona: Producers and consumers of the platform.
Description: With the S3 bucket configuration unchanged and the AWS provider upgraded to version v3.75.0
we see that the versioning
argument has been deprecated.
You can lookup the deprecation warning in the AWS provider documentation(opens in new tab) and see the following notice:
versioning - (Optional, Deprecated) Configuration of the S3 bucket versioning state. See Versioning below for details. Terraform will only perform drift detection if a configuration value is provided. Use the resource
aws_s3_bucket_versioning
instead.
Note
This is one example of many types of changes that need to be analyzed individually as not all changes are the same. Always reference the provider documentation for any warnings presented to you during an upgrade.Step 6: Document error, warning, or change
Task: Document and prioritize issues by creating a ticket in the team backlog.
Persona: Producers and consumers of the platform.
Description: When the speculative plan identifies changes to the infrastructure, it is essential to document these changes and prioritize them for review and implementation. Creating a ticket in the backlog helps organize and track the necessary modifications, ensuring they are addressed in a timely and systematic manner.
Clearly document the changes identified in the speculative plan. Include details such as the specific resources affected, the nature of the actions (e.g., additions, modifications, deletions), and any potential impacts on the infrastructure.
Ensure that the ticket includes all relevant information and context to facilitate understanding and resolution by the assigned engineer (including your future self).
Step 7: Refactor
Task: Refactor Terraform code to ensure the speculative plan reflects no changes.
Persona: Producers and consumers of the platform.
Description: If there are errors, warnings, or changes detected in the speculative plan output, it is essential to refactor(opens in new tab) the Terraform code to align with the latest provider recommendations.
Note
We will rely solely on the speculative plan output to ensure the refactored configuration still behaves as expected.Continuing our example, we start by addressing the versioning
argument deprecation warning.
Deprecated configuration
Given that the versioning
block in the aws_s3_bucket
resource has been deprecated in favor of the aws_s3_bucket_versioning
resource, we must determine what code change will be functionally equivalent to the already deployed infrastructure in AWS.
After reviewing the AWS provider documentation in Step 5, we’ve determined that the following code change should result in no change to the S3 bucket configuration that has already been deployed to AWS in Step 1.
Remove the versioning
argument and add the aws_s3_bucket_versioning
resource:
diff --git a/main.tf b/main.tf
index 85051bb..28b88ca 100644
--- a/main.tf
+++ b/main.tf
@@ -1,10 +1,6 @@
resource "aws_s3_bucket" "testing" {
bucket = "testing-provider-upgrades-bucket-00001"
- versioning {
- enabled = true
- }
-
server_side_encryption_configuration {
rule {
bucket_key_enabled = false
@@ -15,3 +11,11 @@ resource "aws_s3_bucket" "testing" {
}
}
}
+
+resource "aws_s3_bucket_versioning" "testing" {
+ bucket = aws_s3_bucket.testing.id
+
+ versioning_configuration {
+ status = "Enabled"
+ }
+}
Note
When refactoring code, make each change as small as possible in order to address one issue at a time.Next, we’ll verify our assumption by running another speculative plan:
$ terraform plan
aws_s3_bucket.testing: Refreshing state... [id=testing-provider-upgrades-bucket-00001]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_s3_bucket_versioning.testing will be created
+ resource "aws_s3_bucket_versioning" "testing" {
+ bucket = "testing-provider-upgrades-bucket-00001"
+ id = (known after apply)
+ versioning_configuration {
+ mfa_delete = (known after apply)
+ status = "Enabled"
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
╷
│ Warning: Argument is deprecated
│
│ with aws_s3_bucket.testing,
│ on main.tf line 1, in resource "aws_s3_bucket" "testing":
│ 1: resource "aws_s3_bucket" "testing" {
│
│ Use the aws_s3_bucket_server_side_encryption_configuration resource instead
│
│ (and one more similar warning elsewhere)
╵
There are two changes to note:
- A new deprecation warning, suggesting we use the
aws_s3_bucket_server_side_encryption_configuration
resource instead. - The creation of the
aws_s3_bucket_versioning
resource that we added to our configuration.
To ensure there are no errors, warnings, or actions when upgrading to a newer provider version, we need to address both of these changes.
First, we can address the new deprecation warning by updating our configuration to use the aws_s3_bucket_server_side_encryption_configuration
resource. This change is similar to the first deprecation warning about the versioning
argument.
Following the same process we did for the versioning
deprecation warning, we will review the AWS provider documentation to determine a functionally equivalent refactor.
Remove the server_side_encryption_configuration
argument and add the aws_s3_bucket_server_side_encryption_configuration
resource:
diff --git a/main.tf b/main.tf
index 0e3fc1f..b8de40c 100644
--- a/main.tf
+++ b/main.tf
@@ -1,14 +1,5 @@
resource "aws_s3_bucket" "testing" {
bucket = "testing-provider-upgrades-bucket-00001"
-
- server_side_encryption_configuration {
- rule {
- bucket_key_enabled = false
- apply_server_side_encryption_by_default {
- sse_algorithm = "AES256"
- }
- }
- }
}
resource "aws_s3_bucket_versioning" "testing" {
@@ -18,3 +9,15 @@ resource "aws_s3_bucket_versioning" "testing" {
status = "Enabled"
}
}
+
+resource "aws_s3_bucket_server_side_encryption_configuration" "testing" {
+ bucket = aws_s3_bucket.testing.id
+
+ rule {
+ bucket_key_enabled = false
+
+ apply_server_side_encryption_by_default {
+ sse_algorithm = "AES256"
+ }
+ }
+}
Again, we can check the speculative plan output to see if we have addressed the deprecation warning.
$ terraform plan
aws_s3_bucket.testing: Refreshing state... [id=testing-provider-upgrades-bucket-00001]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
~ update in-place
Terraform will perform the following actions:
# aws_s3_bucket.testing will be updated in-place
~ resource "aws_s3_bucket" "testing" {
id = "testing-provider-upgrades-bucket-00001"
tags = {}
# (12 unchanged attributes hidden)
- server_side_encryption_configuration {
- rule {
- bucket_key_enabled = false -> null
- apply_server_side_encryption_by_default {
- sse_algorithm = "AES256" -> null
# (1 unchanged attribute hidden)
}
}
}
# (1 unchanged block hidden)
}
# aws_s3_bucket_server_side_encryption_configuration.testing will be created
+ resource "aws_s3_bucket_server_side_encryption_configuration" "testing" {
+ bucket = "testing-provider-upgrades-bucket-00001"
+ id = (known after apply)
+ rule {
+ bucket_key_enabled = false
+ apply_server_side_encryption_by_default {
+ sse_algorithm = "AES256"
# (1 unchanged attribute hidden)
}
}
}
# aws_s3_bucket_versioning.testing will be created
+ resource "aws_s3_bucket_versioning" "testing" {
+ bucket = "testing-provider-upgrades-bucket-00001"
+ id = (known after apply)
+ versioning_configuration {
+ mfa_delete = (known after apply)
+ status = "Enabled"
}
}
Plan: 2 to add, 1 to change, 0 to destroy.
We have successfully removed all of the deprecation warnings!
However, we now have three changes to address:
- The creation of the
aws_s3_bucket_server_side_encryption_configuration
resource that we added to our configuration. - The creation of the
aws_s3_bucket_versioning
resource that we added to our configuration. - The
aws_s3_bucket
will be updated in-place.
We’ve swapped a deprecation warning for an add
and a change
action in our speculative plan output.
We know the S3 bucket has versioning enabled but it's unclear whether adding the aws_s3_bucket_versioning
resource with terraform apply
will succeed without error; the AWS API might throw an error when we try to update this resource. The same is true for the aws_s3_bucket_server_side_encryption_configuration
resource.
However, since we can be certain the deployed infrastructure matches our updated configuration, importing the resources will align the Terraform state with the infrastructure in AWS, avoiding potential conflicts.
Importing resources
The Terraform registry has a section under each resource which explains how to import it(opens in new tab). For example the aws_s3_bucket_versioning
resource explains how to import using the terraform import(opens in new tab) command or an import block(opens in new tab) (if you are using Terraform v1.5.0
and later).
Note
Unlike theterraform import
command, configuration-driven import using import blocks is preferred since it is predictable, can be automated, and lets you preview an import operation before modifying state.Add the aws_s3_bucket_versioning
resource import block to the configuration.
diff --git a/main.tf b/main.tf
index 85051bb..6aea137 100644
--- a/main.tf
+++ b/main.tf
@@ -1,3 +1,8 @@
+import {
+ to = aws_s3_bucket_versioning.testing
+ id = "testing-provider-upgrades-bucket-00001"
+}
+
resource "aws_s3_bucket" "testing" {
bucket = "testing-provider-upgrades-bucket-00001"
Now we can check the speculative plan output to see if we have addressed the aws_s3_bucket_versioning
resource creation message:
$ terraform plan
aws_s3_bucket.testing: Refreshing state... [id=testing-provider-upgrades-bucket-00001]
aws_s3_bucket_versioning.testing: Preparing import... [id=testing-provider-upgrades-bucket-00001]
aws_s3_bucket_versioning.testing: Refreshing state... [id=testing-provider-upgrades-bucket-00001]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
~ update in-place
Terraform will perform the following actions:
# aws_s3_bucket.testing will be updated in-place
~ resource "aws_s3_bucket" "testing" {
id = "testing-provider-upgrades-bucket-00001"
tags = {}
# (12 unchanged attributes hidden)
- server_side_encryption_configuration {
- rule {
- bucket_key_enabled = false -> null
- apply_server_side_encryption_by_default {
- sse_algorithm = "AES256" -> null
# (1 unchanged attribute hidden)
}
}
}
# (1 unchanged block hidden)
}
# aws_s3_bucket_server_side_encryption_configuration.testing will be created
+ resource "aws_s3_bucket_server_side_encryption_configuration" "testing" {
+ bucket = "testing-provider-upgrades-bucket-00001"
+ id = (known after apply)
+ rule {
+ bucket_key_enabled = false
+ apply_server_side_encryption_by_default {
+ sse_algorithm = "AES256"
# (1 unchanged attribute hidden)
}
}
}
# aws_s3_bucket_versioning.testing will be imported
resource "aws_s3_bucket_versioning" "testing" {
bucket = "testing-provider-upgrades-bucket-00001"
expected_bucket_owner = null
id = "testing-provider-upgrades-bucket-00001"
versioning_configuration {
mfa_delete = null
status = "Enabled"
}
}
Plan: 1 to import, 1 to add, 1 to change, 0 to destroy.
Note
Imports are the only acceptable change to see in a speculative plan when upgrading to a newer provider version.Great! We have successfully refactored the Terraform configuration to address having an add
action in our speculative plan for the aws_s3_bucket_versioning
resource. Now for the remaining add
action that still persists.
Add the aws_s3_bucket_server_side_encryption_configuration
resource import block to the configuration:
diff --git a/main.tf b/main.tf
index 95b467a..5914ada 100644
--- a/main.tf
+++ b/main.tf
@@ -3,6 +3,11 @@ import {
id = "testing-provider-upgrades-bucket-00001"
}
+import {
+ to = aws_s3_bucket_server_side_encryption_configuration.testing
+ id = "testing-provider-upgrades-bucket-00001"
+}
+
resource "aws_s3_bucket" "testing" {
bucket = "testing-provider-upgrades-bucket-00001"
}
Again, we can check the speculative plan output to see if we have addressed the aws_s3_bucket_server_side_encryption_configuration
resource creation message:
aws_s3_bucket.testing: Refreshing state... [id=testing-provider-upgrades-bucket-00001]
aws_s3_bucket_server_side_encryption_configuration.testing: Preparing import... [id=testing-provider-upgrades-bucket-00001]
aws_s3_bucket_versioning.testing: Preparing import... [id=testing-provider-upgrades-bucket-00001]
aws_s3_bucket_server_side_encryption_configuration.testing: Refreshing state... [id=testing-provider-upgrades-bucket-00001]
aws_s3_bucket_versioning.testing: Refreshing state... [id=testing-provider-upgrades-bucket-00001]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_s3_bucket.testing will be updated in-place
~ resource "aws_s3_bucket" "testing" {
id = "testing-provider-upgrades-bucket-00001"
tags = {}
# (12 unchanged attributes hidden)
- server_side_encryption_configuration {
- rule {
- bucket_key_enabled = false -> null
- apply_server_side_encryption_by_default {
- sse_algorithm = "AES256" -> null
# (1 unchanged attribute hidden)
}
}
}
# (1 unchanged block hidden)
}
# aws_s3_bucket_server_side_encryption_configuration.testing will be imported
resource "aws_s3_bucket_server_side_encryption_configuration" "testing" {
bucket = "testing-provider-upgrades-bucket-00001"
expected_bucket_owner = null
id = "testing-provider-upgrades-bucket-00001"
rule {
bucket_key_enabled = false
apply_server_side_encryption_by_default {
kms_master_key_id = null
sse_algorithm = "AES256"
}
}
}
# aws_s3_bucket_versioning.testing will be imported
resource "aws_s3_bucket_versioning" "testing" {
bucket = "testing-provider-upgrades-bucket-00001"
expected_bucket_owner = null
id = "testing-provider-upgrades-bucket-00001"
versioning_configuration {
mfa_delete = null
status = "Enabled"
}
}
Plan: 2 to import, 0 to add, 1 to change, 0 to destroy.
Fantastic! There are no more add
actions left in our speculative plan output.
However, there is still a change
action (update-in-place) for the original S3 bucket after removing the server_side_encryption_configuration
block.
This is certainly unexpected behavior.
Discovering a bug
At this point, we aren’t sure how to solve this challenge but here’s what we could try:
- Importing the S3 bucket just like the other resources to see if that works (it doesn’t).
- Run
terraform apply
“a few times” (doesn’t fix the issue). - Give up?
Instead of giving up, we recommend searching for similar issues posted to the provider's GitHub repository(opens in new tab).
Congratulations, you have found a bug(opens in new tab) in the AWS provider version that you just upgraded to!
In this case, we could pause this effort and wait for the issues(opens in new tab) to(opens in new tab) be(opens in new tab) resolved(opens in new tab), or we can add a lifecycle(opens in new tab) block to the S3 bucket resource that will ignore the changes(opens in new tab).
Lifecycle ignore changes
Since we want to move forward with our changes, we will add a lifecycle
block to ignore the changes, knowing that they don’t represent actual changes to the S3 configuration and are incorrectly being shown in a speculative plan due to a bug in the provider.
Add a lifecycle
block to ignore_changes
for server_side_encryption_configuration
within the aws_s3_bucket
resource block:
diff --git a/main.tf b/main.tf
index 5914ada..dec0b7a 100644
--- a/main.tf
+++ b/main.tf
@@ -10,6 +10,12 @@ import {
resource "aws_s3_bucket" "testing" {
bucket = "testing-provider-upgrades-bucket-00001"
+
+ # Temporary fix for https://github.com/hashicorp/terraform-provider-aws/issues/24153.
+ lifecycle {
+ ignore_changes = [
+ server_side_encryption_configuration
+ ]
+ }
}
resource "aws_s3_bucket_versioning" "testing" {
Note
Adding a `lifecycle` block to ignore changes is a temporary workaround and an additional ticket should be put into the platform team’s backlog to remove this whenever the provider bug has been fixed.Verify the speculative plan has all errors, warnings, and actions removed:
$ terraform plan
aws_s3_bucket.testing: Refreshing state... [id=testing-provider-upgrades-bucket-00001]
aws_s3_bucket_versioning.testing: Preparing import... [id=testing-provider-upgrades-bucket-00001]
aws_s3_bucket_server_side_encryption_configuration.testing: Preparing import... [id=testing-provider-upgrades-bucket-00001]
aws_s3_bucket_server_side_encryption_configuration.testing: Refreshing state... [id=testing-provider-upgrades-bucket-00001]
aws_s3_bucket_versioning.testing: Refreshing state... [id=testing-provider-upgrades-bucket-00001]
Terraform will perform the following actions:
# aws_s3_bucket_server_side_encryption_configuration.testing will be imported
resource "aws_s3_bucket_server_side_encryption_configuration" "testing" {
bucket = "testing-provider-upgrades-bucket-00001"
expected_bucket_owner = null
id = "testing-provider-upgrades-bucket-00001"
rule {
bucket_key_enabled = false
apply_server_side_encryption_by_default {
kms_master_key_id = null
sse_algorithm = "AES256"
}
}
}
# aws_s3_bucket_versioning.testing will be imported
resource "aws_s3_bucket_versioning" "testing" {
bucket = "testing-provider-upgrades-bucket-00001"
expected_bucket_owner = null
id = "testing-provider-upgrades-bucket-00001"
versioning_configuration {
mfa_delete = null
status = "Enabled"
}
}
Plan: 2 to import, 0 to add, 0 to change, 0 to destroy.
We no longer have any errors, warnings, or infrastructure changes after upgrading to a newer provider version!
Final result
In summary, here is the final result of the Terraform configuration after our refactoring efforts:
terraform {
required_version = "~> 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "3.75.0"
}
}
}
provider "aws" {}
import {
to = aws_s3_bucket_versioning.testing
id = "testing-provider-upgrades-bucket-00001"
}
import {
to = aws_s3_bucket_server_side_encryption_configuration.testing
id = "testing-provider-upgrades-bucket-00001"
}
resource "aws_s3_bucket" "testing" {
bucket = "testing-provider-upgrades-bucket-00001"
# Temporary fix for https://github.com/hashicorp/terraform-provider-aws/issues/24153.
lifecycle {
ignore_changes = [
server_side_encryption_configuration
]
}
}
resource "aws_s3_bucket_versioning" "testing" {
bucket = aws_s3_bucket.testing.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "testing" {
bucket = aws_s3_bucket.testing.id
rule {
bucket_key_enabled = false
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
Note
After deploying the changes, you can optionally remove import blocks from your configuration or leave them as a record of the resource's origin.Step 8: The changes are reviewed by the team
Task: Submit the refactored Terraform code to the team for review.
Persona: Producers and consumers of the platform.
Description: After refactoring the Terraform code to address deprecated attributes and to ensure there are no actions in the speculative plan, the next step is to submit the updated code for team review. This ensures that the changes are reviewed by peers for accuracy, adherence to best practices, and to catch any potential issues before deployment.
Push the refactored code to the upgrade-aws-provider
branch we checked out in Step 3 and create a pull request(opens in new tab) for the team to review in your VCS provider (for example, GitHub).
Provide a clear and concise description of the changes made, including the reason for the refactor and any relevant context. Mention that you’ve ensured there are no errors, warnings, or actions in the speculative plan.
For example,
This PR refactors the Terraform code to replace the deprecated
versioning
block in theaws_s3_bucket
resource with the recommendedaws_s3_bucket_versioning
resource. While testing, I have found an additional deprecation warning that was unexpected.To address these warnings, two resources were added to align with the latest AWS provider recommendations. These resources will be imported during the next
terraform apply
to ensure there are no unexpected errors.I’ve also found a bug(opens in new tab) in this provider version so a
lifecycle
block has been added to ignore misleading changes in the plan output.Please review for accuracy and adherence to best practices.
Respond to any comments or feedback provided by the reviewers. Make necessary adjustments to the code based on the review and update the pull request accordingly.
Step 9: The changes are merged into the main git branch
Task: Merge the approved refactored code into the main branch.
Persona: Producers and consumers of the platform.
Description: After the refactored Terraform code has been reviewed and approved by the team, the next step is to merge the changes into the main branch. This step finalizes the code changes, making them part of the main codebase and preparing them for deployment.
Step 10: Apply terraform configuration
Task: Execute terraform apply
to deploy changes using the upgraded Terraform provider version.
Persona: Producers and consumers of the platform.
Description: After merging the refactored code into the main branch, the final step is to run terraform apply
to deploy the changes using the upgraded provider version. This ensures that the infrastructure is updated and aligned with the new provider version, and any configuration changes are applied.
Depending on how your infrastructure is managed, your changes might be deployed automatically as part of merging your pull request into the main branch. Alternatively, there might be some other process that needs to be approved before deployment.
Refer to your team for how this step should ideally be executed.
Step 11: Verify deployment
Task: Verify the deployment has succeeded without issues.
Persona: Producers and consumers of the platform.
Description: The final step is to verify a successful deployment.
This involves checking that all resources are correctly provisioned and configured according to the updated Terraform state. Ensure that there are no errors or discrepancies, and confirm that the infrastructure behaves as expected with the new provider version.
Conclusion
In conclusion, by following this guide, you have successfully navigated the process of upgrading the AWS provider including:
- Monitoring for new provider releases.
- Updating the provider configuration to use the new version.
- Run speculative plans to identify potential issues.
The example workflow showed common issues that arise:
- Deprecated arguments in the existing configuration.
- Aligning your configuration with the latest provider recommendations.
- Importing resources to maintain state consistency.
- Finding bugs in the provider by searching the repository issues.
Finally, you now understand the importance of peer reviews followed by deployment verification of the upgraded configuration, ensuring a smooth transition to the new provider version.