Skip to content

Automate Policy Updates

Add approved images to BIMP policy from your image ingestion pipeline.

Integrate BIMP with your image ingestion and approval pipeline so that every newly approved image becomes an image mapping automatically.

You can update internal mappings with the BIMP API or manage an external bimp-mappings.json file in source control. Choose one source of truth for each policy phase. BIMP does not allow API edits to mappings managed by an external source.

For guidance on structuring Policy Groups and phases, see Policy Management.

Define the information your ingestion pipeline will provide for every approved image:

  • the existing image reference used as the From image;
  • the newly approved, exact Target image reference;
  • the reason for the update;
  • any operating system and architecture scope; and
  • the Policy Groups and phases that should receive the mapping.

Targets must be exact image references. Validate the image, tag, digest, and platform compatibility before updating BIMP.

Use this approach when BIMP owns the mappings and your ingestion pipeline should publish each update without a separate BIMP review.

Create an API key with permission to read Policy Groups and phases, write image mappings, and publish policy. See Access the BIMP API for setup and secret-handling guidance.

Set the values used by the examples:

Terminal window
export BIMP_API_URL="https://<your-bimp-host>"
export BIMP_ORGANIZATION_ID="<organization-id>"
export BIMP_FROM_IMAGE="registry.example.com/python:3.12.1"
export BIMP_TARGET_IMAGE="registry.example.com/python:3.12.2"
export BIMP_MAPPING_REASON="Approved by the image ingestion pipeline"
# Load BIMP_API_KEY from your secret manager.

Do not store the API key in the pipeline definition, source repository, or build log.

List the organization’s Policy Groups:

Terminal window
curl --fail-with-body --silent --show-error \
--header "x-api-key: $BIMP_API_KEY" \
--header "accept: application/json" \
"$BIMP_API_URL/api/v1/org/$BIMP_ORGANIZATION_ID/policy-groups" \
| jq '.items[] | {id, name, applyToAllRepos}'

Inspect a Policy Group to find its phases, source types, and published mappings:

Terminal window
export BIMP_POLICY_GROUP_ID="<policy-group-id>"
curl --fail-with-body --silent --show-error \
--header "x-api-key: $BIMP_API_KEY" \
--header "accept: application/json" \
"$BIMP_API_URL/api/v1/org/$BIMP_ORGANIZATION_ID/policy-groups/"\
"$BIMP_POLICY_GROUP_ID/details" \
| jq '{phases, publishedMappings}'

Select every Policy Group whose published policy uses the previous approved image and record the internal phase that owns incremental updates. Keep this destination list in pipeline configuration so an image is not added to an unrelated Policy Group. External phases cannot be edited with the mappings API.

The examples below use a JSON destination list:

Terminal window
export BIMP_POLICY_DESTINATIONS='[
{"groupId":"<first-policy-group-id>","phaseId":"<first-phase-id>"},
{"groupId":"<second-policy-group-id>","phaseId":"<second-phase-id>"}
]'

Use the following request to add the mapping to one internal policy phase. The same command can run in GitHub Actions or another CI/CD system after its secret manager exposes BIMP_API_KEY to the job:

Terminal window
export BIMP_POLICY_GROUP_ID="<policy-group-id>"
export BIMP_POLICY_PHASE_ID="<policy-phase-id>"
curl --fail-with-body --silent --show-error \
--request POST \
--header "x-api-key: $BIMP_API_KEY" \
--header "accept: application/json" \
--header "content-type: application/json" \
--data "$(
jq -n \
--arg fromImage "$BIMP_FROM_IMAGE" \
--arg toImage "$BIMP_TARGET_IMAGE" \
--arg reason "$BIMP_MAPPING_REASON" \
'{fromImage: $fromImage, toImage: $toImage, reason: $reason}'
)" \
"$BIMP_API_URL/api/v1/org/$BIMP_ORGANIZATION_ID/policy-groups/"\
"$BIMP_POLICY_GROUP_ID/phases/$BIMP_POLICY_PHASE_ID/mappings"

The request creates a draft mapping. Publish the updated Policy Group with a second request:

Terminal window
curl --fail-with-body --silent --show-error \
--request POST \
--header "x-api-key: $BIMP_API_KEY" \
--header "accept: application/json" \
"$BIMP_API_URL/api/v1/org/$BIMP_ORGANIZATION_ID/policy-groups/"\
"$BIMP_POLICY_GROUP_ID/publish"

Configure the GitHub Actions job to fail when either command exits with an error. The --fail-with-body option returns a failing exit code while retaining the BIMP error response for troubleshooting.

For each destination, post the mapping to the phase and then publish its Policy Group:

Terminal window
printf '%s' "$BIMP_POLICY_DESTINATIONS" | jq -c '.[]' |
while IFS= read -r destination; do
group_id="$(printf '%s' "$destination" | jq -r '.groupId')"
phase_id="$(printf '%s' "$destination" | jq -r '.phaseId')"
jq -n \
--arg fromImage "$BIMP_FROM_IMAGE" \
--arg toImage "$BIMP_TARGET_IMAGE" \
--arg reason "$BIMP_MAPPING_REASON" \
'{fromImage: $fromImage, toImage: $toImage, reason: $reason}' |
curl --fail-with-body --silent --show-error \
--request POST \
--header "x-api-key: $BIMP_API_KEY" \
--header "accept: application/json" \
--header "content-type: application/json" \
--data-binary @- \
"$BIMP_API_URL/api/v1/org/$BIMP_ORGANIZATION_ID/policy-groups/"\
"$group_id/phases/$phase_id/mappings"
curl --fail-with-body --silent --show-error \
--request POST \
--header "x-api-key: $BIMP_API_KEY" \
--header "accept: application/json" \
"$BIMP_API_URL/api/v1/org/$BIMP_ORGANIZATION_ID/policy-groups/"\
"$group_id/publish"
done

Publishing makes the complete Policy Group draft live, not only the mapping created by this run. Ensure the group has no unrelated draft changes before the pipeline publishes it.

The API updates one Policy Group at a time. A failure can leave earlier groups published and later groups unchanged. Make the pipeline safe to retry by checking the phase’s draft mappings before creating a mapping. If the same From image and platform already exist, verify or update that mapping instead of creating a duplicate.

The publish endpoint does not immediately open remediation pull or merge requests. Repositories follow their normal remediation routines.

Continuously update an external mapping file

Section titled “Continuously update an external mapping file”

Use this approach when source control and your existing approval workflow should own the mapping set.

Create an external policy phase and connect it to a bimp-mappings.json file on a branch that BIMP can access. Once the external source is active, changes merged into the configured branch are synchronized into published policy automatically. The pipeline does not need to call the BIMP publish API for each file update.

The file must contain an object with a mappings array:

{
"mappings": [
{
"fromImage": "registry.example.com/python:3.12.1",
"toImage": "registry.example.com/python:3.12.2",
"reason": "Approved by the image ingestion pipeline"
},
{
"fromImage": "registry.example.com/node:22.1.0",
"toImage": "registry.example.com/node:22.2.0",
"platformScope": "linux/amd64",
"reason": "Approved Node.js image"
}
]
}

Each mapping requires fromImage and toImage. reason, platformScope, mappingContext, applicableDateTime, bypassCooldown, and order are optional. Targets must be exact; From image values can use supported wildcards.

BIMP treats the file as the complete source for that phase. Every pipeline run must preserve existing mappings and add or update the relevant entry. Do not replace the file with only the newest mapping, because mappings omitted from a subsequent version are removed from that phase.

After approving an image, check out the branch that contains bimp-mappings.json. Pass the previous image, approved image, reason, and optional platform scope to the job as environment variables:

Terminal window
export BIMP_FROM_IMAGE="registry.example.com/python:3.12.1"
export BIMP_TARGET_IMAGE="registry.example.com/python:3.12.2"
export BIMP_MAPPING_REASON="Approved by the image ingestion pipeline"
export BIMP_PLATFORM_SCOPE=""

Use jq to update the matching entry or append it when it does not exist. A mapping is identified by its From image and platform scope:

Terminal window
mapping_file="bimp-mappings.json"
updated_file="$(mktemp)"
jq \
--arg fromImage "$BIMP_FROM_IMAGE" \
--arg toImage "$BIMP_TARGET_IMAGE" \
--arg reason "$BIMP_MAPPING_REASON" \
--arg platformScope "${BIMP_PLATFORM_SCOPE:-}" \
'
.mappings = (
if any(
.mappings[];
.fromImage == $fromImage and
(.platformScope // "") == $platformScope
)
then [
.mappings[] |
if .fromImage == $fromImage and
(.platformScope // "") == $platformScope
then . + {toImage: $toImage, reason: $reason}
else .
end
]
else .mappings + [
{fromImage: $fromImage, toImage: $toImage, reason: $reason} +
if $platformScope == ""
then {}
else {platformScope: $platformScope}
end
]
end
)
' "$mapping_file" > "$updated_file"
jq -e '.mappings | type == "array"' "$updated_file" > /dev/null
mv "$updated_file" "$mapping_file"

This preserves unrelated mappings and makes repeated runs with the same source and platform update the existing entry instead of adding a duplicate. Run the commands with set -euo pipefail so the job stops before committing when the file is missing, invalid, or cannot be updated.

The following workflow accepts an approved image update, modifies bimp-mappings.json, and opens a pull request against main. Adapt the trigger and branch name to match your ingestion pipeline:

name: Update BIMP image mappings
on:
workflow_dispatch:
inputs:
from_image:
description: Current approved image reference
required: true
type: string
target_image:
description: New approved image reference
required: true
type: string
reason:
description: Reason for the update
required: true
type: string
platform_scope:
description: Optional platform, such as linux/amd64
required: false
type: string
permissions:
contents: write
pull-requests: write
jobs:
update-policy:
runs-on: ubuntu-latest
env:
BIMP_FROM_IMAGE: ${{ inputs.from_image }}
BIMP_TARGET_IMAGE: ${{ inputs.target_image }}
BIMP_MAPPING_REASON: ${{ inputs.reason }}
BIMP_PLATFORM_SCOPE: ${{ inputs.platform_scope }}
steps:
- name: Check out the policy repository
uses: actions/checkout@v6
with:
ref: main
- name: Update the mapping file
shell: bash
run: |
set -euo pipefail
mapping_file="bimp-mappings.json"
updated_file="$(mktemp)"
jq \
--arg fromImage "$BIMP_FROM_IMAGE" \
--arg toImage "$BIMP_TARGET_IMAGE" \
--arg reason "$BIMP_MAPPING_REASON" \
--arg platformScope "${BIMP_PLATFORM_SCOPE:-}" \
'
.mappings = (
if any(
.mappings[];
.fromImage == $fromImage and
(.platformScope // "") == $platformScope
)
then [
.mappings[] |
if .fromImage == $fromImage and
(.platformScope // "") == $platformScope
then . + {toImage: $toImage, reason: $reason}
else .
end
]
else .mappings + [
{
fromImage: $fromImage,
toImage: $toImage,
reason: $reason
} +
if $platformScope == ""
then {}
else {platformScope: $platformScope}
end
]
end
)
' "$mapping_file" > "$updated_file"
jq -e '.mappings | type == "array"' "$updated_file" > /dev/null
mv "$updated_file" "$mapping_file"
- name: Open a pull request
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
branch="bimp/image-update-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
git config user.name "github-actions[bot]"
git config user.email \
"41898282+github-actions[bot]@users.noreply.github.com"
git switch -c "$branch"
git add bimp-mappings.json
if git diff --cached --quiet; then
echo "The mapping is already current."
exit 0
fi
git commit -m "Update approved image mapping"
git push --set-upstream origin "$branch"
gh pr create \
--base main \
--head "$branch" \
--title "Update approved image mapping" \
--body "Updates bimp-mappings.json from the image ingestion pipeline."

GitHub documents the GITHUB_TOKEN permission model, the official actions/checkout action, and the gh pr create command.

The repository or organization must allow GitHub Actions to create pull requests. If the workflow updates a different private repository, the built-in GITHUB_TOKEN is not sufficient because it is scoped to the repository that contains the workflow. Use a narrowly scoped GitHub App installation token or another organization-approved credential in that case. See GitHub’s guidance for authenticating workflows.

The complete automation flow is:

  1. validate and approve the new image;
  2. read the current bimp-mappings.json from the configured branch;
  3. add or update the mapping without removing unrelated entries;
  4. validate the resulting JSON and ensure each target is exact;
  5. commit the change directly or open a pull or merge request; and
  6. merge the change into the branch monitored by BIMP.

Use a pull or merge request when policy changes require human approval. Commit directly only when the ingestion pipeline already provides the required approval controls.

After the change is merged, check the external phase in BIMP to confirm the latest synchronization succeeded. Organization or Policy Group cooldown settings may delay when an automatically synchronized update becomes active.