Manage actions¶
See also: Juju | Action
Many charms rely on Juju actions to complete their setup or to expose operational procedures (e.g. show-proxied-endpoints, pre-refresh-check, resume-refresh). The Terraform Provider for Juju lets you run an action as part of a Terraform plan and use its output to drive other resources.
Note
The action is run and its result awaited during the creation of the resource. The action’s output is set as a computed field that can be used by other resources after the resource has been created.
Run an action¶
To run an action, in your Terraform plan create a resource of the juju_action type, specifying the model, the application to run the action on, the name of the action, and the unit to target, in specific (e.g. traefik/0) or leader (e.g. traefik/leader) notation. For example:
resource "juju_action" "show_proxied_endpoints" {
model_uuid = juju_model.development.uuid
application_name = juju_application.traefik.name
action_name = "show-proxied-endpoints"
unit = "traefik/0"
}
See more:
juju_action(resource)
Run an action with arguments¶
Some actions accept arguments. To pass them, in the juju_action resource definition add an args map with the key=value pairs the action expects. For example:
resource "juju_action" "backup" {
model_uuid = juju_model.development.uuid
application_name = juju_application.postgresql.name
action_name = "backup"
unit = "postgresql/leader"
args = {
backup_dir = "/var/backups/postgresql"
}
}
Run an action and use its output¶
The action’s result is exposed as the output computed attribute, a JSON string. Use jsondecode() to extract values from it and feed them into other resources or locals. For example:
resource "juju_action" "show_proxied_endpoints" {
model_uuid = juju_model.development.uuid
application_name = juju_application.traefik.name
action_name = "show-proxied-endpoints"
unit = "traefik/leader"
}
locals {
application_proxied_url = jsondecode(jsondecode(juju_action.show_proxied_endpoints.output)["proxied-endpoints"])["traefik"].url
}
The output attribute is always a JSON string, so the first jsondecode() turns it into a Terraform map you can index into. Some charms, however, return a value that is itself a JSON string — for example, the traefik charm sets the proxied-endpoints key to a JSON-encoded string rather than a nested object, in which case a second jsondecode() is needed. Check your charm’s action documentation to find out which shape it returns.
Run an action on every unit¶
By default a juju_action targets a single unit. To run the same action on every unit of an application, use for_each over the application’s unit_numbers set — a computed attribute that lists the numbers of the deployed units (e.g. ["0", "1", "2"]):
resource "juju_application" "myapp" {
model_uuid = juju_model.development.uuid
name = "myapp"
charm {
name = "mycharm"
channel = "latest/stable"
}
units = 3
}
resource "juju_action" "do_something" {
for_each = juju_application.myapp.unit_numbers
model_uuid = juju_model.development.uuid
application_name = juju_application.myapp.name
action_name = "do-something"
unit = "${juju_application.myapp.name}/${each.value}"
}
Each element of unit_numbers is a string (e.g. "0", "1"), so the unit argument is built by joining the application name and the unit number with a /. Terraform creates one juju_action resource per unit — juju_action.do_something["0"], juju_action.do_something["1"], and so on — and runs the action on each.
Note
unit_numbers is only populated once the application has been created and its units are available. Because juju_action.do_something references juju_application.myapp.unit_numbers, Terraform waits for the application to be created before enqueuing the actions.
Re-run an action¶
A juju_action resource only runs when it is created. Once the action has completed and its result is stored in the Terraform state, subsequent terraform apply runs are a no-op — the action is not re-executed. This is by design: Juju actions are one-shot operations, and the resource represents the result of a single execution.
To force the action to run again, you must replace the resource. Because every attribute of juju_action uses RequiresReplace, any change to the config causes Terraform to destroy and recreate the resource — that is, re-run the action. For example, you can change the resource’s name (and any references to it) or, without editing the plan, target the resource for replacement on a single apply:
terraform apply -replace="juju_action.show_proxied_endpoints"
Run an action on every apply¶
Due to actions being one-shot operations, if you wish to run the action again on every re-apply, we suggest driving the action’s replacement from a terraform_data resource whose input is timestamp() via the replace_triggered_by lifecycle directive:
resource "terraform_data" "replacement" {
input = timestamp()
}
resource "juju_action" "action" {
model_uuid = juju_model.development.uuid
application_name = juju_application.postgresql.name
action_name = "backup"
unit = "postgresql/leader"
args = {
backup_dir = "/var/backups/postgresql"
}
lifecycle {
replace_triggered_by = [
terraform_data.replacement
]
}
}
Because timestamp() evaluates to a new value on every apply, terraform_data.replacement changes every run, which triggers a replacement of the juju_action resource — re-running the action on every terraform apply.
Caution
Only use this pattern when the action is genuinely idempotent or when re-running it is safe. Re-running an action that mutates state (e.g. a backup that overwrites the previous one) may have side effects.
Manually trigger an action¶
The Run an action on every apply pattern re-runs the action unconditionally on each apply. If you want finer control — to re-run the action only when you explicitly decide to — drive the replacement from a terraform_data resource whose input you bump manually:
resource "terraform_data" "trigger" {
input = 1 # bump this value to re-run the action
}
resource "juju_action" "do_something" {
model_uuid = juju_model.development.uuid
application_name = juju_application.myapp.name
action_name = "do-something"
unit = "myapp/leader"
lifecycle {
replace_triggered_by = [
terraform_data.trigger
]
}
}
As long as input stays the same, terraform apply is a no-op for the action. When you want to re-run the action, change the value (e.g. from 1 to 2) and apply. Terraform detects that terraform_data.trigger has changed, which triggers a replacement of juju_action.do_something — re-running the action. This gives you an explicit trigger without editing the action’s own configuration.
Order an action relative to other resources¶
Terraform infers an implicit dependency when a juju_action references another resource, so the action is always run after the application it targets has been created. For example, the application_name reference below makes Terraform wait for juju_application.traefik to exist before enqueuing the action:
resource "juju_action" "show_proxied_endpoints" {
model_uuid = juju_model.development.uuid
application_name = juju_application.traefik.name # implicit dependency
action_name = "show-proxied-endpoints"
unit = "traefik/0"
}
However, Terraform cannot infer dependencies that aren’t expressed through references. If the action must run only after some other condition is met — for example, after an integration has been created, after a config has been applied, or after another action has completed — add an explicit depends_on:
resource "juju_integration" "db" {
model_uuid = juju_model.development.uuid
application {
name = juju_application.postgresql.name
endpoint = "db"
}
application {
name = juju_application.myapp.name
endpoint = "db"
}
}
resource "juju_action" "migrate" {
model_uuid = juju_model.development.uuid
application_name = juju_application.myapp.name
action_name = "migrate"
unit = "myapp/leader"
# Run the migration only after the database integration is in place.
depends_on = [juju_integration.db]
}
Remove an action¶
To remove an action, remove its resource definition from your Terraform plan.
See more:
juju_action(resource)