<a id="manage-actions"></a>

# Manage actions

> See also: [Juju | Action](https://canonical.com/juju/docs/juju-cli/latest/reference/action/#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:

```terraform
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)](https://documentation.ubuntu.com/terraform-provider-juju/latest/reference/terraform-provider/resources/action.md)

### 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:

```terraform
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:

```terraform
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"]`):

```terraform
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:

```bash
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:

```terraform
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:

```terraform
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:

```terraform
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`:

```terraform
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)](https://documentation.ubuntu.com/terraform-provider-juju/latest/reference/terraform-provider/resources/action.md)
