Create modular, reusable workflows that act as building blocks for shared automation across projects, enabling one workflow to run another as a job. Before you begin, ensure you have workflow creation permissions within your component and understand basic workflow creation concepts.
Reusable workflow access to caller workflow vars or secrets is not automatic. Pass these values as inputs or enable inheritance with vars: inherit or secrets: inherit in the caller workflow.
|
Create the reusable workflow structure
A reusable workflow must include a workflow_call trigger and at least one job.
Set up workflow_call trigger
To create the basic reusable workflow structure:
-
Create a new YAML file in your repository’s
.cloudbees/workflows/directory. -
Configure the
workflow_calltrigger with inputs definition:apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: reusable-workflow on: workflow_call: inputs: var-1: type: string required: false env-prod: type: string required: true
Define input parameters
Configure input parameters to accept values from caller workflows:
-
Specify input parameter types:
-
string: Text-based values
-
boolean: True or false values
-
number: Numerical values
-
-
Set required field to
truefor mandatory parameters orfalsefor optional parameters. -
Add descriptions for each parameter to clarify usage.
Configure inheritance patterns
Reusable workflows support two approaches for accessing caller workflow values: inheritance and explicit mapping.
Enable secrets inheritance
To automatically inherit all secrets from the caller workflow:
-
In the caller workflow, add
secrets: inheritto the job configuration:jobs: caller-job: uses: .cloudbees/workflows/a-reusable-workflow.yaml secrets: inherit
Enable vars inheritance
To automatically inherit all variables from the caller workflow:
-
In the caller workflow, add
vars: inheritto the job configuration:jobs: caller-job: uses: .cloudbees/workflows/a-reusable-workflow.yaml vars: inherit -
The reusable workflow can now access all variables from the caller job’s scope, including environment-scoped variables.
Set up explicit mapping
To provide specific secrets or variables to the reusable workflow:
-
Map individual secrets in the caller workflow:
jobs: caller-job: uses: .cloudbees/workflows/a-reusable-workflow.yaml secrets: secret-1: ${{ secrets.secret-A }} secret-2: ${{ secrets.secret-B }} -
Map individual variables in the caller workflow:
jobs: caller-job: uses: .cloudbees/workflows/a-reusable-workflow.yaml vars: var-1: ${{ vars.var-A }} var-2: ${{ vars.var-B }}
Configure environment parameter passing
To enable environment access in reusable workflows:
-
Pass the environment name as an input in the caller workflow:
inputs: env-prod: production -
Use the environment input in the reusable workflow job:
jobs: job-rw-2: environment: ${{ inputs.env-prod }}
If a job specifies an environment using an expression (for example, ${{ inputs.env-prod }}), first resolve the environment name and then evaluate the vars and secrets accessible in the reusable workflow (depending on the vars and secrets inheritance defined in the caller workflow), since their values can come from that environment.
This uses a delayed expression evaluation model.
|
Implement security configurations
Understand and apply security restrictions for reusable workflows.
Apply cross-organization restrictions
Cross-organization security restrictions ensure environments are only accessible to workflows in the same SCM organization:
-
If a reusable workflow references an environment, the reusable workflow must reside within the same SCM organization as the caller workflow.
-
Reusable workflows in different SCM organizations must rely solely on vars and secrets passed as inputs explicitly by the caller.
-
The caller maintains full control over vars and secrets visible to cross-organization reusable workflows.
Configure environment-specific access
To enable environment-scoped access:
-
The caller job must have
secrets: inheritandvars: inheritenabled to provide access to environment secrets and vars. -
Environment approvals and OIDC token issuance apply when the job targets that environment.
-
Environment-specific values override inherited values if both are defined.
Handle OIDC token considerations
When using environments with OIDC tokens:
-
Automatic inheritance could pose security risks if reusable workflow jobs can assume roles associated with environments.
-
Use explicit mapping to control which environment variables and secrets are exposed to reusable workflows.
-
Consider OIDC subject claim risks when designing inheritance patterns.
Create the caller workflow
Configure caller workflows to use reusable workflows with proper path syntax and inheritance settings.
Configure the uses field
Specify the reusable workflow using absolute path syntax:
-
Use the format:
{owner}/{repo}/.cloudbees/workflows/{filename} -
Pin to specific references:
{owner}/{repo}/.cloudbees/workflows/{filename}@{ref}Examples:
jobs: caller-job: uses: .cloudbees/workflows/a-reusable-workflow.yaml # Default branch # OR uses: .cloudbees/workflows/a-reusable-workflow.yaml@main # Specific branch # OR uses: .cloudbees/workflows/a-reusable-workflow.yaml@v1.0 # Specific tag # OR uses: .cloudbees/workflows/a-reusable-workflow.yaml@abc123 # Specific commit
Configuration examples and patterns
The following examples demonstrate different inheritance and mapping patterns.
Example 1: Secrets inheritance with environment input
This pattern inherits all secrets from the caller and passes an environment name.
# File: .cloudbees/workflows/call-inner-workflow.yaml apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: caller-workflow on: push: branches: [ main ] jobs: caller-job: uses: .cloudbees/workflows/a-reusable-workflow.yaml secrets: inherit inputs: var-1: ${{ vars.var-A }} env-prod: production
# File: .cloudbees/workflows/a-reusable-workflow.yaml apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: reusable-workflow on: workflow_call: inputs: var-1: type: string required: false env-prod: type: string required: true jobs: job-rw-1: steps: - name: Use inherited secrets run: | # Inherited secrets are available; vars are not. if [ -n "${{ secrets.secret-1 }}" ]; then echo "secret-1 available"; fi if [ -n "${{ secrets.secret-2 }}" ]; then echo "secret-2 available"; fi # 'var-1' is only available via inputs, not as a var: if [ -n "${{ inputs.var-1 }}" ]; then echo "var-1 provided as input"; fi job-rw-2: environment: ${{ inputs.env-prod }} steps: - name: Use environment-resolved secrets (override if present) run: | # If the environment defines secret-1/secret-2, those values override inherited ones. if [ -n "${{ secrets.secret-1 }}" ]; then echo "secret-1 available (env may override)"; fi if [ -n "${{ secrets.secret-2 }}" ]; then echo "secret-2 available (env may override)"; fi
Example 2: Secrets mapping with environment input
This pattern maps only specific secrets to the reusable workflow.
# File: .cloudbees/workflows/call-inner-workflow.yaml apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: caller-workflow on: push: branches: [ main ] jobs: caller-job: uses: .cloudbees/workflows/a-reusable-workflow.yaml secrets: secret-1: ${{ secrets.secret-A }} secret-2: ${{ secrets.secret-B }} inputs: var-1: ${{ vars.var-A }} env-prod: production
# File: .cloudbees/workflows/a-reusable-workflow.yaml apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: reusable-workflow on: workflow_call: inputs: var-1: type: string required: false env-prod: type: string required: true jobs: job-rw-1: steps: - name: Use mapped secrets and input run: | if [ -n "${{ secrets.secret-1 }}" ]; then echo "secret-1 available"; fi if [ -n "${{ secrets.secret-2 }}" ]; then echo "secret-2 available"; fi if [ -n "${{ inputs.var-1 }}" ]; then echo "var-1 provided as input"; fi job-rw-2: environment: ${{ inputs.env-prod }} steps: - name: Use environment-resolved secrets (override if present) run: | # If the environment defines secret-A/secret-B, those values override the caller mappings. if [ -n "${{ secrets.secret-1 }}" ]; then echo "secret-1 available"; fi if [ -n "${{ secrets.secret-2 }}" ]; then echo "secret-2 available"; fi
Example 3: Variables inheritance with environment input
This pattern inherits all variables from the caller workflow.
# File: .cloudbees/workflows/call-inner-workflow.yaml apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: caller-workflow on: push: branches: [ main ] jobs: caller-job: uses: .cloudbees/workflows/a-reusable-workflow.yaml vars: inherit inputs: var-1: ${{ vars.var-A }} env-prod: production
# File: .cloudbees/workflows/a-reusable-workflow.yaml apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: reusable-workflow on: workflow_call: inputs: var-1: type: string required: false env-prod: type: string required: true jobs: job-rw-1: steps: - name: Use inherited vars and input run: | # Inherited vars are available; secrets are not. if [ -n "${{ vars.var-A }}" ]; then echo "var-A available"; fi if [ -n "${{ vars.var-B }}" ]; then echo "var-B available"; fi # 'var-1' is also available via inputs: if [ -n "${{ inputs.var-1 }}" ]; then echo "var-1 provided as input"; fi job-rw-2: environment: ${{ inputs.env-prod }} steps: - name: Use environment-resolved vars (override if present) run: | # If the environment defines var-A/var-B, those values override inherited ones. if [ -n "${{ vars.var-A }}" ]; then echo "var-A available (env may override)"; fi if [ -n "${{ vars.var-B }}" ]; then echo "var-B available (env may override)"; fi
Example 4: Variables mapping with environment input
This pattern maps only specific variables to the reusable workflow.
# File: .cloudbees/workflows/call-inner-workflow.yaml apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: caller-workflow on: push: branches: [ main ] jobs: caller-job: uses: .cloudbees/workflows/a-reusable-workflow.yaml vars: var-1: ${{ vars.var-A }} var-2: ${{ vars.var-B }} inputs: var-1: ${{ vars.var-A }} env-prod: production
# File: .cloudbees/workflows/a-reusable-workflow.yaml apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: reusable-workflow on: workflow_call: inputs: var-1: type: string required: false env-prod: type: string required: true jobs: job-rw-1: steps: - name: Use mapped vars and input run: | # Mapped vars are available; secrets are not. if [ -n "${{ vars.var-1 }}" ]; then echo "var-1 available (mapped)"; fi if [ -n "${{ vars.var-2 }}" ]; then echo "var-2 available (mapped)"; fi # 'var-1' is also available via inputs: if [ -n "${{ inputs.var-1 }}" ]; then echo "var-1 provided as input"; fi job-rw-2: environment: ${{ inputs.env-prod }} steps: - name: Use environment-resolved vars (override if present) run: | # If the environment defines var-A/var-B, those values override the mapped ones. if [ -n "${{ vars.var-1 }}" ]; then echo "var-1 available (env may override)"; fi if [ -n "${{ vars.var-2 }}" ]; then echo "var-2 available (env may override)"; fi
Example 5: Full inheritance pattern
This pattern inherits both variables and secrets from the caller workflow.
# File: .cloudbees/workflows/call-inner-workflow.yaml apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: caller-workflow on: push: branches: [ main ] jobs: caller-job: uses: .cloudbees/workflows/a-reusable-workflow.yaml vars: inherit secrets: inherit inputs: var-1: ${{ vars.var-A }} env-prod: production
# File: .cloudbees/workflows/a-reusable-workflow.yaml apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: reusable-workflow on: workflow_call: inputs: var-1: type: string required: false env-prod: type: string required: true jobs: job-rw-1: steps: - name: Use inherited vars and secrets (plus input) run: | # Inherited vars are available: if [ -n "${{ vars.var-A }}" ]; then echo "var-A available (inherited)"; fi if [ -n "${{ vars.var-B }}" ]; then echo "var-B available (inherited)"; fi # Inherited secrets are available: if [ -n "${{ secrets.secret-1 }}" ]; then echo "secret-1 available (inherited)"; fi if [ -n "${{ secrets.secret-2 }}" ]; then echo "secret-2 available (inherited)"; fi # 'var-1' is also available via inputs: if [ -n "${{ inputs.var-1 }}" ]; then echo "var-1 provided as input"; fi job-rw-2: environment: ${{ inputs.env-prod }} steps: - name: Use environment-resolved vars and secrets (override if present) run: | # If the environment defines var-A/var-B, those values override inherited ones: if [ -n "${{ vars.var-A }}" ]; then echo "var-A available (env may override)"; fi if [ -n "${{ vars.var-B }}" ]; then echo "var-B available (env may override)"; fi # If the environment defines secret-1/secret-2, those values override inherited ones: if [ -n "${{ secrets.secret-1 }}" ]; then echo "secret-1 available (env may override)"; fi if [ -n "${{ secrets.secret-2 }}" ]; then echo "secret-2 available (env may override)"; fi
Example 6: Complete mapping pattern
This pattern explicitly maps both variables and secrets to the reusable workflow.
# File: .cloudbees/workflows/call-inner-workflow.yaml apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: caller-workflow on: push: branches: [ main ] jobs: caller-job: uses: .cloudbees/workflows/a-reusable-workflow.yaml vars: var-1: ${{ vars.var-A }} var-2: ${{ vars.var-B }} secrets: secret-1: ${{ secrets.secret-A }} secret-2: ${{ secrets.secret-B }} inputs: var-1: ${{ vars.var-A }} env-prod: production
# File: .cloudbees/workflows/a-reusable-workflow.yaml apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: reusable-workflow on: workflow_call: inputs: var-1: type: string required: false env-prod: type: string required: true jobs: job-rw-1: steps: - name: Use mapped vars and secrets (plus input) run: | # MAPPED VARS (from caller var-A/var-B): if [ -n "${{ vars.var-1 }}" ]; then echo "var-1 available (mapped)"; fi if [ -n "${{ vars.var-2 }}" ]; then echo "var-2 available (mapped)"; fi # MAPPED SECRETS (from caller secret-A/secret-B): if [ -n "${{ secrets.secret-1 }}" ]; then echo "secret-1 available (mapped)"; fi if [ -n "${{ secrets.secret-2 }}" ]; then echo "secret-2 available (mapped)"; fi # INPUT (explicitly passed): if [ -n "${{ inputs.var-1 }}" ]; then echo "var-1 provided as input"; fi job-rw-2: environment: ${{ inputs.env-prod }} steps: - name: Use environment-resolved vars and secrets (override if present) run: | # If the environment defines var-A/var-B, they override mapped var-1/var-2 values: if [ -n "${{ vars.var-1 }}" ]; then echo "var-1 available (env may override)"; fi if [ -n "${{ vars.var-2 }}" ]; then echo "var-2 available (env may override)"; fi # If the environment defines secret-A/secret-B, they override mapped secret-1/secret-2 values: if [ -n "${{ secrets.secret-1 }}" ]; then echo "secret-1 available (env may override)"; fi if [ -n "${{ secrets.secret-2 }}" ]; then echo "secret-2 available (env may override)"; fi
Best practices and limitations
Follow these guidelines when creating reusable workflows:
Security-first approach
-
Prioritize explicit mapping over inheritance when security is critical.
-
Use inheritance only when you trust the reusable workflow with all caller secrets and variables.
-
Consider OIDC subject claim risks when environments are involved.
Organizational boundaries
-
Understand cross-organizational restrictions for environment access.
-
Design workflows to work within same SCM organization limits when using environments.
-
Use explicit input passing for cross-organizational reusable workflows.