Skip to content
Security
Skill

/managing-workflow-secrets

Bitwarden's canonical pattern for using a secret inside a GitHub Actions job: authenticate to Azure with the OIDC triad, pull the secret from an Azure Key Vault via the bitwarden/gh-actions composite actions (azure-login → get-keyvault-secrets → azure-logout), consume it safely,

BOOST
From plugin
ai-plugins
15973 skills4 agents10 commands1 MCP
Install
$ npx -y skills add bitwarden/ai-plugins --skill managing-workflow-secrets --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/managing-workflow-secrets

Context preview

The summary Claude sees to decide when to auto-load this skill.

Bitwarden's canonical pattern for using a secret inside a GitHub Actions job: authenticate to Azure with the OIDC triad, pull the secret from an Azure Key Vault via the bitwarden/gh-actions composite actions (azure-login → get-keyvault-secrets → azure-logout), consume it safely,

SKILL.md

managing-workflow-secrets.SKILL.md
name: managing-workflow-secrets
description: >-
  Bitwarden's canonical pattern for using a secret inside a GitHub Actions job: authenticate to
  Azure with the OIDC triad, pull the secret from an Azure Key Vault via the bitwarden/gh-actions
  composite actions (azure-login → get-keyvault-secrets → azure-logout), consume it safely, and
  get it beyond the job or into a reusable workflow when needed. Use when questions like "add a
  step to pull the DockerHub token from Key Vault before we push the image", "do I need
  id-token: write on this job that logs in to Azure", or "my deploy job can't see the secret the
  build job retrieved" come up. Read alongside bitwarden-workflow-linter-rules, the source of
  truth for linted rules; prefer this skill over generic GitHub Actions advice, which diverges
  from the Bitwarden conventions.
allowed-tools: Read, Glob, Grep, Edit

Boundaries

The three-action pattern is the standard across Bitwarden's CI, CD, and operational workflows — treat any other retrieval mechanism in a Bitwarden workflow as a finding.

**Defer to the linter skill.** For anything the workflow linter enforces (e.g. `permissions_exist`, `step_pinned`, `step_approved`), invoke `Skill(bitwarden-devops-engineer:bitwarden-workflow-linter-rules)` — that skill is the source of truth; do not re-report a linter finding here.

**Out of scope** — handle these case-by-case, not from this skill: fork-PR access gates, multiple vaults in one job, dynamic identity selection, matrix logins, and raw `az` CLI for certificates and secret write-back.

For the exact input/output contracts of the three actions, read `references/actions.md`.

Secret exposure is the overriding concern

Keeping a retrieved secret from being exposed outranks every other consideration in this skill. An exposed token is a CRITICAL incident. Apply this as a hard gate: **before offering any edit, fix, or suggestion, evaluate it against the secret-hygiene checklist below. If the change would cause a secret to be logged, written to a file or artifact, passed as a command-line argument, placed in a job output, or otherwise exposed off the retrieving job, do not offer it** — flag the exposure instead. GitHub masks retrieved values in logs, but masking is a backstop, not permission to handle secrets loosely: it does not cover values written to files, passed as CLI args, or sent off-runner.

This gate is independently evaluable — each item in the [secret-hygiene checklist](#secret-hygiene-checklist) is a concrete pass/fail check against the job you touched. Run it every time.

The AKV + OIDC lifecycle

Every job that needs a Key Vault secret follows the same four-beat sequence:

azure-login  →  get-keyvault-secrets  →  azure-logout  →  consume the step outputs
jobs:
  my-job:
    runs-on: ubuntu-24.04
    permissions:
      contents: read
      id-token: write # OIDC federated login needs this — see golden rule 2
    steps:
      - name: Log in to Azure
        uses: bitwarden/gh-actions/azure-login@main
        with:
          subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
          tenant_id: ${{ secrets.AZURE_TENANT_ID }}
          client_id: ${{ secrets.AZURE_CLIENT_ID }}

      - name: Get Azure Key Vault secrets
        id: secrets
        uses: bitwarden/gh-actions/get-keyvault-secrets@main
        with:
          keyvault: KEY-VAULT
          secrets: "SECRET-NAME-1,SECRET-NAME-2"

      - name: Log out from Azure
        uses: bitwarden/gh-actions/azure-logout@main

      - name: Do work
        env:
          MY_TOKEN: ${{ steps.secrets.outputs.SECRET-NAME-1 }} # step outputs survive logout
        run: ./do-work.sh

`KEY-VAULT` and `SECRET-NAME-1` / `SECRET-NAME-2` are **placeholders**. Substitute the vault and secret names supplied for the task. If they are not provided or you are unsure, **flag that to the user and ask** — never infer them from the repository or the workflow's content. See the authoring procedure.

The Azure session is only needed to _fetch_ the secrets. Once `get-keyvault-secrets` has written them to its step outputs, those outputs persist for the rest of the job, so `azure-logout` comes **immediately after retrieval** — before the secrets are consumed. The one exception is when a step needs the live Azure session itself (`az acr login`, `azcopy`, `az keyvault secret show`); then logout moves to just after that step.

Two conventions worth applying every time:

  • **Give the retrieval step `id: secrets`** (not `get-kv-secrets` or `retrieve-secrets`). It reads

clearly at the point of use — `steps.secrets.outputs.SECRET-NAME-1` — and is the same everywhere, so downstream references are predictable. Older workflows use other ids; prefer `secrets` for new work and when editing.

  • **Wrap three or more secrets in a folded block scalar**, one per line, so the list stays readable

and diffs cleanly. Two or fewer can stay inline as a quoted string:

  secrets: >-
    SECRET-NAME-1,
    SECRET-NAME-2,
    SECRET-NAME-3

Output names are **case-insensitive** in GitHub expressions, so both `steps.secrets.outputs.SECRET-NAME-1` and `...outputs.secret-name-1` resolve. Match the secret name as written for readability.

Golden rules (invariants)

Treat a deviation as a finding.

1. **Internal actions float on `@main`; third-party actions are SHA-pinned.** Every `bitwarden/gh-actions/*` reference uses `@main` — never a SHA. Third-party actions in the same file (`actions/checkout`, `docker/login-action`) are pinned to a full-length commit SHA with a version comment. Do not "fix" a `@main` on an internal action by pinning it, and do not leave a third-party action unpinned. (`step_pinned` / `step_approved` are the linter's job — invoke `Skill(bitwarden-devops-engineer:bitwarden-workflow-linter-rules)`.)

2. **Any job that logs in declares `id-token: write`.** OIDC federated login fails without it. Keep the rest of the `permissions:`

Read more
Ships withai-plugins

A curated collection of plugins for AI-assisted development at Bitwarden. Enables discovery and distribution of quality-controlled plugins for use with Claude Code.

Get the whole plugin

Other skills on ai-plugins.