Agent Native

provider-ephemeral-resources

Implement Terraform ephemeral resources using the Plugin Framework to handle sensitive values like tokens and credentials that must never be written to state…

by HashiCorpOfficial publisherNot reviewed

Identity proven by GitHub's verified organisation badge (hashicorp)

Agents
Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode, claude.ai and API
License
MPL-2.0 (copyleft)
Pin
f706481pinned Oct 9, 2026
Context cost
about 2,100 tokenswhen the skill loads
GitHub stars
890on hashicorp/agent-skills
Scripts
None found

What it does

“Implement Terraform provider ephemeral resources with the Plugin Framework: the Open/Renew/Close lifecycle, ephemeral schema design, registration via EphemeralResources, renewal for expiring credentials, and how ephemeral values flow into write-only attributes and provider configuration. Use when adding an ephemeral resource, exposing secrets/tokens/certificates that must never persist in state or plan, deciding between an ephemeral resource and a data source, or wiring short-lived credentials from one provider into another.”
The author's description, from SKILL.md

Generated summary from the skill's own text

Implement Terraform ephemeral resources using the Plugin Framework to handle sensitive values like tokens and credentials that must never be written to state or plan files. The skill covers the Open/Renew/Close lifecycle, schema design, registration, and integration with provider configuration and write-only attributes.

When to use it

  • Exposing short-lived tokens or credentials at apply time only
  • Wiring sensitive values from one provider into another
  • Handling STS-style tokens that expire during Terraform operations
  • Implementing lease renewal and revocation for temporary credentials

When not to

  • Reading non-sensitive data, use a data source instead
  • Sensitive values that downstream resources must store as attributes
  • Credentials without expiration or renewal capabilities

Install

These commands install from the publisher's repository and need no account.

skills CLI

npx skills add hashicorp/agent-skills --skill provider-ephemeral-resources

Installs from the repository's default branch, which may have changed since the commit we checked.

GitHub CLI 2.90 or later

gh skill install hashicorp/agent-skills provider-ephemeral-resources@f706481af9b8fedb66de909f6243ad29601afa0c

Installs the commit we checked (f706481).

Copy by hand

Download the folder at f706481 and put it in .claude/skills/provider-ephemeral-resources for Claude Code and in .agents/skills/provider-ephemeral-resources for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode.

Skill folder on GitHub at f706481

Copy for your agent

A prompt for any coding agent. It installs this skill from GitHub at the pinned commit, copies the files as they are, and runs nothing unless you ask.

Prompt

Install the agent skill "provider-ephemeral-resources" by HashiCorp.

Source, pinned to commit f706481af9b8fedb66de909f6243ad29601afa0c:
https://github.com/hashicorp/agent-skills/tree/f706481af9b8fedb66de909f6243ad29601afa0c/plugins/terraform/skills/provider-ephemeral-resources

1. Download that folder at exactly this commit. Do not use the default branch.
2. Copy every file unchanged, keeping the folder structure, into .claude/skills/provider-ephemeral-resources/ for Claude Code and .agents/skills/provider-ephemeral-resources/ for Codex, Cursor, Gemini CLI, GitHub Copilot and OpenCode. Create the folders if they do not exist.
3. Do not edit the files, and do not run any script in the folder unless I ask you to.
4. When you are done, list the files you copied and where you put them.

Listing and checks: https://www.agentnative.dev/skills/hashicorp/provider-ephemeral-resources

SKILL.md at the pin

---
name: provider-ephemeral-resources
description: >-
  Implement Terraform provider ephemeral resources with the Plugin
  Framework: the Open/Renew/Close lifecycle, ephemeral schema design,
  registration via EphemeralResources, renewal for expiring credentials,
  and how ephemeral values flow into write-only attributes and provider
  configuration. Use when adding an ephemeral resource, exposing
  secrets/tokens/certificates that must never persist in state or plan,
  deciding between an ephemeral resource and a data source, or wiring
  short-lived credentials from one provider into another.
license: MPL-2.0
metadata:
  lifecycle-status: active
  copyright: Copyright IBM Corp. 2026
  version: "0.0.1"
---

# Terraform Provider Ephemeral Resources

Ephemeral resources (Terraform 1.10+) produce values that are **never
persisted to state or plan**. They exist for exactly one job: handing
secrets — tokens, generated passwords, short-lived certificates, decrypted
values — to the parts of a configuration that need them, without writing
them to disk. Any data source that returns a sensitive value is a candidate
to be (or to also exist as) an ephemeral resource.

Official docs: [Ephemeral Resources](https://developer.hashicorp.com/terraform/plugin/framework/ephemeral-resources).

## When to Use One

| Situation | Use |
|---|---|
| Read-only lookup of non-sensitive data | Data source |
| Value is sensitive and only needed at apply time (DB password for a provider block, token for a write-only attribute) | Ephemeral resource |
| Sensitive value that downstream *managed resources* must store (e.g. as an attribute) | Regular resource/data source — but pair with write-only attributes where possible |
| Credential that expires mid-operation (STS-style tokens, short-TTL leases) | Ephemeral resource with `Renew` |

Ephemeral results can be used in provider configuration, write-only
attributes, provisioner configuration, and other ephemeral contexts — but
not in regular attributes, because those persist to state.

## Lifecycle

Terraform calls up to three methods per operation:

- **`Open`** (required) — fetch or create the value; runs during plan
  and/or apply whenever the result is needed. There is no state to refresh
  and nothing to import.
- **`Renew`** (optional) — called when the wall clock passes the
  `RenewAt` returned by `Open`/`Renew`, for values that expire while
  Terraform is still running. Renew cannot return a new result — it can
  only extend/refresh what `Open` produced (e.g. re-lease the same
  credential); if the value itself changes on renewal, the API is not
  renewable in this sense and `Open` must return a longer-lived value.
- **`Close`** (optional) — called when Terraform is done with the value;
  revoke leases or delete temporary credentials here.

`Open` can pass bytes forward via `resp.Private`; `Renew` and `Close`
receive them — use this for lease IDs needed to renew/revoke.

## Implementation

```go
var (
    _ ephemeral.EphemeralResource              = &tokenEphemeralResource{}
    _ ephemeral.EphemeralResourceWithConfigure = &tokenEphemeralResource{}
    _ ephemeral.EphemeralResourceWithRenew     = &tokenEphemeralResource{}
    _ ephemeral.EphemeralResourceWithClose     = &tokenEphemeralResource{}
)

func NewTokenEphemeralResource() ephemeral.EphemeralResource {
    return &tokenEphemeralResource{}
}

type tokenEphemeralResource struct {
    client *examplecloud.Client
}

type tokenEphemeralResourceModel struct {
    RoleName types.String `tfsdk:"role_name"`
    Token    types.String `tfsdk:"token"`
    LeaseID  types.String `tfsdk:"lease_id"`
}

func (r *tokenEphemeralResource) Metadata(_ context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) {
    resp.TypeName = req.ProviderTypeName + "_token"
}

func (r *tokenEphemeralResource) Schema(_ context.Context, _ ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) {
    resp.Schema = schema.Schema{
        Attributes: map[string]schema.Attribute{
            "role_name": schema.StringAttribute{
                Required:            true,
                MarkdownDescription: "Role to obtain a token for.",
            },
            "token": schema.StringAttribute{
                Computed:            true,
                Sensitive:           true,
                MarkdownDescription: "The issued token. Never persisted to state.",
            },
            "lease_id": schema.StringAttribute{
                Computed:            true,
                MarkdownDescription: "Identifier of the token lease.",
            },
        },
    }
}

func (r *tokenEphemeralResource) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
    var data tokenEphemeralResourceModel
    resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
    if resp.Diagnostics.HasError() {
        return
    }

    lease, err := r.client.IssueToken(ctx, data.RoleName.ValueString())
    if err != nil {
        resp.Diagnostics.AddError(
            "Error opening Token",
            fmt.Sprintf("issuing token for role (%s): %s", data.RoleName.ValueString(), err),
        )
        return
    }

    data.Token = types.StringValue(lease.Token)
    data.LeaseID = types.StringValue(lease.ID)

    resp.RenewAt = lease.ExpiresAt.Add(-2 * time.Minute) // renew with margin
    resp.Private.SetKey(ctx, "lease_id", []byte(lease.ID))
    resp.Diagnostics.Append(resp.Result.Set(ctx, &data)...)
}

func (r *tokenEphemeralResource) Renew(ctx context.Context, req ephemeral.RenewRequest, resp *ephemeral.RenewResponse) {
    leaseID, diags := req.Private.GetKey(ctx, "lease_id")
    resp.Diagnostics.Append(diags...)
    if resp.Diagnostics.HasError() {
        return
    }

    lease, err := r.client.RenewLease(ctx, string(leaseID))
    if err != nil {
        resp.Diagnostics.AddError("Error renewing Token", err.Error())
        return
    }
    resp.RenewAt = lease.ExpiresAt.Add(-2 * time.Minute)
}

func (r *tokenEphemeralResource) Close(ctx context.Context, req ephemeral.CloseRequest, resp *ephemeral.CloseResponse) {
    leaseID, diags := req.Private.GetKey(ctx, "lease_id")
    resp.Diagnostics.Append(diags...)
    if resp.Diagnostics.HasError() {
        return
    }

    if err := r.client.RevokeLease(ctx, string(leaseID)); err != nil {
        resp.Diagnostics.AddError("Error closing Token", err.Error())
    }
}
```

`Configure` follows the same ProviderData-cast pattern as resources (the
`provider-resources` skill, if available, shows it); the client comes from
`resp.EphemeralResourceData` set in the provider's `Configure`.

## Registration

The provider opts in via `provider.ProviderWithEphemeralResources`:

```go
var _ provider.ProviderWithEphemeralResources = &examplecloudProvider{}

func (p *examplecloudProvider) EphemeralResources(_ context.Context) []func() ephemeral.EphemeralResource {
    return []func() ephemeral.EphemeralResource{
        NewTokenEphemeralResource(),
    }
}
```

Set `resp.EphemeralResourceData = client` in the provider's `Configure`
alongside `ResourceData`/`DataSourceData`.

## Design Rules

- **Never log the value, never put it in a diagnostic.** The whole point is
  non-persistence; an error message containing the token defeats it.
- Mark the secret attribute `Sensitive: true` anyway — it guards rendering
  in the ephemeral value's own lifecycle output.
- No plan modifiers, no import, no `id` convention — there is no state for
  any of them to act on.
- Schema inputs follow the same rules as data source arguments; expose the
  API's identifiers (`role_name`), not invented ones.
- Set `RenewAt` with a safety margin before the real expiry; Terraform
  renews lazily, not on a precise timer.
- If the upstream value cannot be revoked, skip `Close` rather than
  implementing a no-op that suggests revocation happens.

## Testing

Ephemeral results never reach state, so tests assert them indirectly — the
standard pattern echoes the ephemeral value through the `echoprovider` into
a regular resource the test can inspect. Minimum coverage: a basic
open-and-use test and per-attribute tests alongside required fields. Use
the `provider-test-patterns` skill (if available) — its ephemeral testing
reference covers the echoprovider setup, version gating
(`tfversion.SkipBelow(tfversion.Version1_10_0)`), and multi-step patterns.

## Documentation

Registry docs live at `docs/ephemeral-resources/<name>.md`, generated by
`tfplugindocs` like every other page type. Use the `provider-docs` skill
(if available) for the workflow; document the renewal/revocation behavior
explicitly — users need to know whether closing their Terraform run revokes
the credential.

## Checklist

- [ ] Value genuinely must not persist (otherwise a data source is simpler)
- [ ] `Open` implemented; `Renew`/`Close` only where the API supports them
- [ ] Secret attributes `Sensitive: true`; value never logged or in diagnostics
- [ ] Lease/handle passed via `Private`, not via the result
- [ ] `RenewAt` set with margin for expiring credentials
- [ ] Registered in `EphemeralResources()`; `EphemeralResourceData` set in provider Configure
- [ ] Echo-provider acceptance tests, version-gated to Terraform >= 1.10
- [ ] Docs page explains lifetime, renewal, and revocation behavior

From hashicorp/agent-skills at f706481, shown as plain text under its license: MPL-2.0 (copyleft). The license text is in plugins/terraform/skills/provider-ephemeral-resources/SKILL.md.

View SKILL.md on GitHub at f706481

Files

1 file, 9.3 KB in total. Each file has a sha256 in the pin's manifest; the first 12 characters are shown.

Skill folder on GitHub at f706481
All 19 skills from HashiCorp

More from HashiCorp

  • aws-ami-builder

    This skill enables the agent to build custom Amazon Machine Images using Packer's amazon-ebs builder.

    Claude Code, Codex, Cursor +4 · about 870 tokens · pinned 6 days ago

  • azure-image-builder

    This skill uses Packer's Azure ARM builder to create custom managed images and Compute Gallery images in Azure.

    Claude Code, Codex, Cursor +4 · about 1K tokens · pinned 6 days ago

  • azure-verified-modules

    This skill provides comprehensive requirements and best practices for developing Azure Terraform modules that meet Azure Verified Modules (AVM) certification…

    Claude Code, Codex, Cursor +4 · about 4K tokens · pinned 6 days ago

  • new-terraform-provider

    Scaffolds a new Terraform provider using the Plugin Framework by creating workspace structure, initializing a Go module, and generating main.go and…

    Contains scripts

    Claude Code, Codex, Cursor +3 · about 330 tokens · pinned 6 days ago

  • provider-actions

    This skill guides implementing Terraform Actions, imperative operations that execute at provider lifecycle events using the Plugin Framework.

    Claude Code, Codex, Cursor +4 · about 4.2K tokens · pinned 6 days ago

  • provider-configuration

    This skill guides implementing Terraform provider authentication using the Plugin Framework, covering provider schema design, credential provider chains that…

    Claude Code, Codex, Cursor +4 · about 3.3K tokens · pinned 6 days ago

All Cloud and DevOps skills
  • google-agents-cli-deploy

    Deploys agents built with agents-cli to Agent Runtime, Cloud Run, or GKE using a tested CLI pipeline.

    by GoogleOfficial publisher

    Claude Code, Codex, Cursor +4 · about 7.4K tokens · pinned 6 days ago

  • azure-to-aws

    This skill guides agents through migrating Azure workloads to AWS across six phases: discovering Azure resources from Terraform, live CLI, app code, and…

    by Amazon (AWS)Official publisher

    Contains scripts

    Claude Code, Codex, Cursor +3 · about 4.3K tokens · pinned 6 days ago

  • gcp-to-aws

    Migrates workloads from Google Cloud Platform to AWS, including AI and agentic applications from any provider.

    by Amazon (AWS)Official publisher

    Contains scripts

    Claude Code, Codex, Cursor +3 · about 14.3K tokens · pinned 6 days ago

  • heroku-to-aws

    This skill guides migration of applications and databases from Heroku to AWS services like Elastic Beanstalk, RDS, ElastiCache, and MSK.

    by Amazon (AWS)Official publisher

    Contains scripts

    Claude Code, Codex, Cursor +3 · about 7K tokens · pinned 6 days ago

  • tf-best-practices

    This skill provides authoring rules for generating secure AWS Terraform and runs a read-only policy gate to verify generated code against those rules.

    by Amazon (AWS)Official publisher

    Contains scripts

    Claude Code, Codex, Cursor +3 · about 3K tokens · pinned 6 days ago

  • provider-framework-migration

    This skill guides migrating Terraform provider resources from Plugin SDKv2 to the Plugin Framework incrementally using muxing, where both plugins run side by…

    by HashiCorpOfficial publisher

    Claude Code, Codex, Cursor +4 · about 2K tokens · pinned 6 days ago

The skills listed here are third-party software, collected from public GitHub repositories as a catalog of recent skills and provided as is. Agent Native does not write or control them and is not responsible for any harm, loss or damage caused by installing or using them. Labels and checks describe what we saw at one commit and are not a guarantee. Read a skill's files before you run it. Terms of use