Terraform Hidden Gems! Secret Rotation with time_rotating

Secret management in IaC: Learn how to manage secrets using the time_rotating resource.

Daniel Jimenez Garcia
Cloud Native Daily

--

Over the years Terraform amassed a large number of features across its language and providers. As teams leverage Terraform to solve typical and not-so-typical infrastructure as code problems, it’s easy to overlook some of those features. Let’s take a look at some of them, starting with the time_rotating resource.

Photo by Kenny Eliason on Unsplash

Secret management is a topic that any team implementing an infrastructure as code strategy will come across. Sooner or later you will find some of the infrastructure managed by Terraform requires and/or generates secrets. And this brings its own set of considerations and challenges, like being able to rotate those secrets.

This is where the time_rotating Terraform resource can be of help.

How the time_rotating resource works

In itself, this is nothing but a simple resource that forces a recreation of itself whenever a given period has passed. Once recreated, it will stay put until the given period passes again, and so on.

For example, the following resource will be recreated every 10 days:

resource "time_rotating" "secrets_rotation" {
rotation_days = 10
}

This means after creating the resource, there will be no changes on every terraform plan for the next 10 days. Once 10 days since its creation have passed, the next terraform plan will show the resource needs to be recreated.

This is easier to see with a shorter period, like rotating_minutes = 5.

  • If you run terraform plan before 5 minutes since its creation, you will see no changes.
  • But if you run terraform plan after 5 minutes since its creation, you will see:
Terraform will perform the following actions:

# time_rotating.secrets_rotation will be created
+ resource "time_rotating" "secrets_rotation" {
+ day = (known after apply)
+ hour = (known after apply)
+ id = (known after apply)
+ minute = (known after apply)
+ month = (known after apply)
+ rfc3339 = (known after apply)
+ rotation_minutes = 5
+ rotation_rfc3339 = (known after apply)
+ second = (known after apply)
+ unix = (known after apply)
+ year = (known after apply)
}

Plan: 1 to add, 0 to change, 0 to destroy.

How you can use it to rotate secrets

The power of the time_rotating resource comes when you combine with other resources, as a dependency, so recreating the time_rotating forces a recreation of the dependent resource.

This is easier seen with a couple of examples. Let’s get down to it.

Imagine you are creating an Azure AD application, with its own application password. You can rotate that password after 10 days by combining a time_rotating resource with the azuread_application_password resource:

resource "time_rotating" "password_rotation" {
rotation_days = 10
}

resource "azuread_application_password" "app_password" {
application_object_id = azuread_application.my_application.object_id
rotate_when_changed = {
rotation = time_rotating.password_rotation.id
}
}

We are forcing the password to be regenerated by setting the rotate_when_changed property of the azuread_application_password resource to the id of the time_rotating resource.
After the desired period, terraform recreates the time_rotating resource, which causes it to get a new id, which in turn forces the AD application password to be regenerated:

Terraform will perform the following actions:

# azuread_application_password.app_password must be replaced
-/+ resource "azuread_application_password" "app_password" {
+ display_name = (known after apply)
~ end_date = "2025-08-28T15:05:32Z" -> (known after apply)
~ id = "24478106-fee9-4b2d-bcba-ca01d49833f1/password/a3eda3cb-6c4b-4f0c-8d44-51c37a448eb6" -> (known after apply)
~ key_id = "a3eda3cb-6c4b-4f0c-8d44-51c37a448eb6" -> (known after apply)
~ rotate_when_changed = { # forces replacement
- "rotation" = "2023-08-28T15:05:30Z"
} -> (known after apply) # forces replacement
~ start_date = "2023-08-28T15:05:32Z" -> (known after apply)
~ value = (sensitive value)
# (1 unchanged attribute hidden)
}

Let’s see a second example. Imagine you are generating a Databricks Token using Terraform. You could similarly rotate that token by say 5 days, combining a time_rotating resource with a databricks_token resource:

resource "time_rotating" "token_rotation" {
rotation_days = 5
}

resource "databricks_token" "cicd_token" {
comment = "CICD environment token (created: ${time_rotating.token_rotation.rfc3339})"
# Token is valid for 6 days but is rotated after 5 days as per time_rotating above.
# This assumes you run `terraform apply` very frequently, or at least daily!
lifetime_seconds = 6 * 24 * 60 * 60
}

In this case, we are setting the dependency by using time_rotating to build the comment of the token. Since a change in the comment forces a recreation of the token, after 5 days the time_rotating resource changes, which forces a change on the comment, which in turn forces terraform to recreate the token.

It’s also worth pointing out that the token lifetime needs to be a little longer than the time_rotating period. How much longer depends on how often you run terraform apply. For example, if the rotation is set as 5 days while the token expires in 6 days, you then have a window of 24h in which to run terraform apply and generate a new token before the old one expires.

This is one of the reasons why tokens are troublesome from a security perspective (fixed lifetime that cant be invalidated once created), and why short-lived tokens are preferred when possible.
In the particular case of databricks tokens for CICD, you might want to consider a service principal whose password you rotate as part of your infra as code, generating a short-lived token on-demand using the principal credentials. See https://github.com/databricks/run-notebook/#use-the-service-principal-in-your-github-workflow

As the examples demonstrate, the time_rotatingresource can be a simple and built-in way to automate the rotation of secrets managed by Terraform.

Happy coding!

--

--

Daniel Jimenez Garcia
Cloud Native Daily

Principal engineer @oliverwyman. Author @DotNetCurry and @DorksKaizen. Interested in Vue, Node, Python, .NET, Kubernetes, Terraform, DevOps and cloud