Skip to main content
Version: v5.7.0

The PingOne Advanced Identity Cloud pack (packs:ping-aic)

packs:ping-aic

The packs:ping-aic entitlement gates opschain-pack-ping-aic, the signed commercial content pack for PingOne Advanced Identity Cloud (formerly ForgeRock Identity Cloud): a tenant's configuration as YAML resources your repository commits, reconciled by ping_aic:plan / apply / export, and the tenant operations a governed change composes — environment promotion, environment secrets and variables, identities, service accounts, tenant logs. The pack is hand-typed Go over the tenant's REST APIs via its own pingaic tool — no SDK, no vendor CLI, no interpreter — executed by the runner, with outbound HTTPS from the runner to the tenant as the only wire. The pack's signed manifest names requires: packs:ping-aic, so the entitlement in your licence must spell exactly that; like every packs:<name> grant it is validated by shape and minted per customer (see entitlements).

The pack lives in the opschain-pack-ping family repository and mounts like any other action pack — a packs: entry in the template's .opschain/packs.yaml naming the pack remote, the ping-aic path and a pinned revision (opschain docs getting-started/2-4-using-packs) — or installs into the pack store as a signed bundle. A mount name is an action namespace and may carry only lowercase letters, digits and underscores, so the pack mounts as ping_aic and its actions read ping_aic:plan, ping_aic:promotion_promote, and so on. Both doors enforce the manifest: installing a signed bundle verifies its signature and its required entitlement, and running any ping_aic:* action without packs:ping-aic refuses with the named pack refusal before anything dials the tenant.

The tenant and its identity​

Every action targets a ping_tenant document — the tenant FQDN, its environment tier (dev, staging or prod), the realms the declarative resources cover, the environment variables the service account arrives in, and whether the tenant opts into the gated api lane:

# .opschain/ping/tenant.yaml
kind: ping_tenant
name: acme-dev
spec:
tenant: openam-acme-dev.forgeblocks.com
tier: dev
realms: [alpha]
service_account:
id_env: PING_AIC_SA_ID
private_key_env: PING_AIC_SA_PRIVATE_KEY
api_lane: disabled

Commit one document per tenant environment and point each OpsChain environment at its own through the ping_aic.tenant_file property. Tenant environments then map onto OpsChain environments: path-based authorisation governs who may touch production, and an approval policy on the environment governs the promotion into it — the pack never invents an approval of its own.

The pack dials the tenant as a service account. Its id and private key (JWK or PEM) reach the step as environment the platform resolved from vault references — opschain.env properties on the OpsChain environment (opschain docs getting-started/3-5-secrets-and-the-vault):

{"opschain": {"env": {
"PING_AIC_SA_ID": "secret-vault://ping/dev/sa_id",
"PING_AIC_SA_PRIVATE_KEY": "secret-vault://ping/dev/sa_private_key"
}}}

Per step the tool mints a JWT-bearer assertion, exchanges it for a short-lived access token and keeps the token in memory only. Each dial logs one value-free line naming which credential variables were present (kind and length only); a variable still carrying an unresolved vault reference refuses the dial by name. Tenant logs authenticate with the log API key pair the same way (PING_AIC_LOG_API_KEY / PING_AIC_LOG_API_SECRET).

Declarative resources​

A resource is one YAML document — kind, name, realm for realm-scoped kinds, and spec, the tenant object's own JSON expressed in YAML. Thirty-two kinds, in the order apply writes them:

KindScopeTenant object
ping_aic_esvtenantan environment variable (type: variable) or secret (type: secret — the value only ever comes from value_env, a vault-resolved environment variable, and is never read back)
ping_aic_secret_mappingrealma secret store mapping from a secret id the configuration names to the ESV aliases holding its value — never a value
ping_aic_scriptrealma script; script is plain source in YAML and base64 on the wire; _id pins the identity
ping_aic_custom_nodetenanta custom node type from the node designer; script is plain source; _id pins the identity journeys reference
ping_aic_journeyrealman authentication journey with every node it references — spec.tree plus spec.nodes — as one promotable document
ping_aic_oauth2_clientrealman OAuth 2.0 client
ping_aic_servicerealma realm service's top-level configuration
ping_aic_realm_configrealmone of the realm's configuration objects (authentication) — replaced, never created or deleted
ping_aic_authz_policy_setrealman authorization policy set with its policies and the resource types they use, as one document
ping_aic_saml_entityrealma SAML 2.0 entity provider, hosted or remote — its configuration and its metadata
ping_aic_cookie_domainstenantthe tenant's cookie domains, one document named cookie-domains
ping_aic_corstenantthe CORS configuration — the service's global settings, its named configurations and the identity-management filter — one document named cors-config
ping_aic_csptenantthe Content Security Policy — the enforced and report-only policies — one document named csp
ping_aic_custom_policytenanta custom validation policy of the policy service — its settings and its function — named by its policyId; one entry of the tenant's policy configuration
ping_aic_managed_objecttenantone object of the managed-object configuration
ping_aic_field_policyrealmthe password (field) policy of a managed object type (alpha_user), the realm its prefix
ping_aic_internal_roletenantan internal role with its privileges, named by name with _id pinned; the system roles are never listed or pruned
ping_aic_org_privilegestenantone organisation-privileges object (alphaOrgPrivileges, bravoOrgPrivileges, privilegeAssignments) — replaced, never created or deleted
ping_aic_idm_configtenantone flat identity-management configuration object by id (access, audit, authentication, external.email, the remote connector servers, selfservice.kba, ui-configuration, or any other) — replaced, never created or deleted
ping_aic_connectortenanta connector (provisioner) configuration
ping_aic_sync_mappingtenantone synchronisation mapping
ping_aic_email_templatetenantan e-mail template
ping_aic_endpointtenanta custom identity-management endpoint with its source inline; the tenant's own endpoints are never listed or pruned
ping_aic_scheduletenanta scheduler job, an invoked script's source inline
ping_aic_iga_workflowtenantan identity-governance workflow definition with its steps inline, named by name; an apply publishes it; the tenant's immutable workflows are never listed, replaced or pruned
ping_aic_service_objecttenanta managed object every environment holds with a fixed id (a role, a group, a service user), named <object type>:<id>
ping_aic_localetenantthe hosted pages' translation overrides for one locale
ping_aic_terms_and_conditionstenantthe terms and conditions — every version with its translations inline — one document named terms-conditions; replaced, never created or deleted
ping_aic_themerealmone hosted-pages theme
ping_aic_telemetrytenantone telemetry provider, named <category>:<id>; its request headers come from the step environment, are never compared and never exported
ping_aic_raw_configtenantany other configuration object, named by its tenant-relative path with : for /
ping_aic_config_metadatatenantthe configuration metadata — the stamp recording which release the tenant carries — one document named config-metadata, applied last; replaced, never created or deleted

The tenant's own objects are protected: a realm's configuration objects, the cookie domains, the CORS configuration, the Content Security Policy, the organisation privileges, the flat identity-management configuration, the terms and conditions and the configuration metadata are replaced, never created or deleted — prune does not apply and a delete refuses by name. The tenant's built-in endpoints, its system internal roles and its immutable governance workflows are never listed, so never exported or pruned. Service objects and raw configuration are the repository's own selection: nothing on the tenant enumerates them, so an export answers none and prune never deletes one.

The semantics are what make the resources safe to commit and re-apply:

  • A document owns the top-level keys it declares. plan compares those keys with the live object (volatile fields stripped, encrypted values reduced to a marker) and an update sends the live object overlaid with the document, so undeclared keys keep their tenant values and a partial document stays idempotent across plans.
  • ${values.<key>} placeholders take per-environment values from the values input — a JSON object or @file:<path>, defaulting to the environment's ping_aic.values_file property — so one resource set serves dev, staging and prod; a placeholder with no value fails by name.
  • Secrets never sit in YAML: an ESV secret names value_env; exported secrets carry no value; connector credentials are best declared as ESV placeholders the tenant resolves.
  • Deletes are opt-in per kind (prune: ping_aic_journey,ping_aic_script) and scoped to the realms the documents cover.
  • Export is deterministic — <kind>/<realm>/<name>.yaml (_global for tenant-wide kinds), sorted keys — so a repeat export of an unchanged tenant is byte-identical and an export followed by a plan is a no-op: adopting an existing tenant is an export, a review and a commit.

Resources​

Every kind carries a usage page inside the pack — docs/resources/<kind>.md in the pack repository and in the signed bundle — with the same five sections: what the tenant object is (scope, apply order, dependencies on other kinds), the document (an annotated example with every field described, secrets as value_env), how the lane treats it (the plan output for a create, update or delete, what apply sends, what export and adopt write back, promotion behaviour), a complete action file that uses it, and its refusals with the exact messages. The example document sits under examples/ping/resources/<kind>/ and the action under examples/ping/actions/; copy both into your repository and they work as shown. The pack's own tests refuse a kind without its page, example and action, so the index below is complete by construction.

KindScopePurposePage in the pack
ping_aic_esvtenantan environment variable or secret other configuration references through an &{esv.…} placeholder; secrets by value_env only, applied on restartdocs/resources/ping_aic_esv.md
ping_aic_secret_mappingrealma realm's secret store mapping — the secret id the configuration names and the ESV aliases holding its value; never a valuedocs/resources/ping_aic_secret_mapping.md
ping_aic_scriptrealma script a journey node or another extension point runs; plain source in YAML, _id pinned for the journeys that name itdocs/resources/ping_aic_script.md
ping_aic_custom_nodetenanta custom node type from the node designer — its script, outcomes and properties — that a journey in any realm may placedocs/resources/ping_aic_custom_node.md
ping_aic_journeyrealman authentication journey with every node it references, as one promotable documentdocs/resources/ping_aic_journey.md
ping_aic_oauth2_clientrealman OAuth 2.0 / OpenID Connect client — type, redirect URIs, scopes, grant typesdocs/resources/ping_aic_oauth2_client.md
ping_aic_servicerealma realm service's top-level configurationdocs/resources/ping_aic_service.md
ping_aic_realm_configrealmone of the realm's configuration objects — the authentication configuration (default journey, landing URLs, security settings); replaced, never created or deleteddocs/resources/ping_aic_realm_config.md
ping_aic_authz_policy_setrealman authorization policy set with every policy in it and the resource types they use — who may reach what — as one reviewable documentdocs/resources/ping_aic_authz_policy_set.md
ping_aic_saml_entityrealma SAML 2.0 entity provider, hosted or remote, as its configuration and metadata; named by the entity id or the tenant's iddocs/resources/ping_aic_saml_entity.md
ping_aic_cookie_domainstenantthe domains the session cookie is set for; one document, replaced never deleteddocs/resources/ping_aic_cookie_domains.md
ping_aic_corstenantthe cross-origin resource sharing configuration — the service switch, the named origin sets, the identity-management filter; one documentdocs/resources/ping_aic_cors.md
ping_aic_csptenantthe Content Security Policy of the hosted pages — the enforced and report-only policies; one documentdocs/resources/ping_aic_csp.md
ping_aic_custom_policytenanta custom validation policy a managed object's schema names by its policyId — its settings and its validation function; replaced inside the tenant's one policy configuration, other entries kept as founddocs/resources/ping_aic_custom_policy.md
ping_aic_managed_objecttenantone managed object — the schema, relationships and hooks of an identity typedocs/resources/ping_aic_managed_object.md
ping_aic_field_policyrealmthe password policy of a realm's users — the rules every password must satisfy; the realm is the object type's prefixdocs/resources/ping_aic_field_policy.md
ping_aic_internal_roletenantan internal role — the privileges it grants over the tenant's objects; _id pinned for the assignments that reference it; the system roles are never listed or pruneddocs/resources/ping_aic_internal_role.md
ping_aic_org_privilegestenantthe organisation privileges of a realm — delegated administration — and the privilege assignments; replaced, never created or deleteddocs/resources/ping_aic_org_privileges.md
ping_aic_idm_configtenanta flat identity-management configuration object — the access rules, audit, authentication, e-mail provider, remote connector servers, knowledge-based authentication, the UI; replaced, never created or deleteddocs/resources/ping_aic_idm_config.md
ping_aic_connectortenanta connector configuration — how the tenant reaches an external system; credentials as ESV placeholdersdocs/resources/ping_aic_connector.md
ping_aic_sync_mappingtenantone synchronisation mapping between a connector's object type and a managed objectdocs/resources/ping_aic_sync_mapping.md
ping_aic_email_templatetenantan e-mail template with its subject and message per localedocs/resources/ping_aic_email_template.md
ping_aic_endpointtenanta custom identity-management endpoint — a script served at /openidm/endpoint/<name>; the tenant's built-in endpoints are never listed or pruneddocs/resources/ping_aic_endpoint.md
ping_aic_scheduletenanta scheduler job — a reconciliation, a script, a task scan — with an invoked script's source inlinedocs/resources/ping_aic_schedule.md
ping_aic_iga_workflowtenantan identity-governance workflow definition with its steps and scripts inline, published on apply; the tenant's immutable workflows are never listed, replaced or pruneddocs/resources/ping_aic_iga_workflow.md
ping_aic_service_objecttenanta managed object every environment must hold with a fixed id — a service user, a predefined role, a group; never listed, so export answers none and prune never deletes onedocs/resources/ping_aic_service_object.md
ping_aic_localetenantthe hosted pages' translation overrides for one locale, per page bundledocs/resources/ping_aic_locale.md
ping_aic_terms_and_conditionstenantthe terms and conditions users accept — every version with its translations inline, and which is active; one document, replaced never deleteddocs/resources/ping_aic_terms_and_conditions.md
ping_aic_themerealmone hosted-pages theme of a realmdocs/resources/ping_aic_theme.md
ping_aic_telemetrytenantone telemetry provider — a destination for a category of the tenant's logs or metrics; credentials from the step environment, never compared, never exporteddocs/resources/ping_aic_telemetry.md
ping_aic_raw_configtenantany configuration object the typed kinds do not cover, governed by its path; never listed or deleteddocs/resources/ping_aic_raw_config.md
ping_aic_config_metadatatenantthe configuration metadata — the stamp recording which release the tenant carries, applied last of all kinds; one document, replaced never deleteddocs/resources/ping_aic_config_metadata.md

The shipped actions​

Every action takes tenant_file (defaulting to the ping_aic.tenant_file property, else .opschain/ping/tenant.yaml) and publishes its outputs with outputs: passthrough.

  • ping_aic:plan / ping_aic:apply / ping_aic:export — the declarative lane: dir (default .opschain/ping/resources), values, prune, and the rendered plan in plan_file (plus plan_json) as the change's evidence; outputs creates, updates, deletes, unchanged, has_changes, and after apply created / updated / deleted. Export takes kinds and realms.
  • ping_aic:config_export / config_import / config_diff / adopt — the configuration tree: one JSON object per file, global and per-realm, in the layout ForgeRock's sample configuration-manager tooling pulls, as the pack's import and adoption format. A repository already holding a tenant's configuration that way moves under OpsChain governance without conversion; adopt converts a tree into YAML resources and reports directories outside the layout in unmapped, never dropping them silently. config_diff is the drift probe: has_changes tells a workflow whether the committed tree and the tenant differ, and plan_file carries the diff.
  • ping_aic:promotion_lock / promotion_status / promotion_promote / promotion_rollback / promotion_unlock — dialled on the source (lower) environment's tenant: dev promotes into staging, staging into prod, and the step log announces the tiers. promotion_promote takes description, dry_run, unlock_after, wait (default on) and wait_timeout, and lands the tenant's promotion report in report_file; promotion_lock publishes the promotion_id that promotion_unlock releases; promotion_status is the wait predicate.
  • ping_aic:esv_set / esv_list / esv_delete / restart / restart_status — environment secrets and variables. A variable's value may be an input; a secret's value always comes from value_env. esv_list publishes names only. restart applies ESV changes and, with wait, polls until the tenant reports ready; restart_status is the wait predicate.
  • ping_aic:identity_get / identity_search / identity_create / identity_update / identity_delete — managed identities (object_type, default alpha_user), bulk through file (a JSON array or newline-delimited JSON). Outputs are masked by default: e-mail addresses, names, phone numbers and addresses reduce to a shape that identifies nothing while identifiers stay for the next step; a bulk step attempts every record and fails when any failed, with every outcome in output_file.
  • ping_aic:service_account_create / service_account_rotate / service_account_delete — the pack's own bootstrap. The key pair is generated on the runner: the public key set goes to the tenant and the private key is written to key_file (mode 0600) and nowhere else — not a step output, not the log — for a following step to store in the vault.
  • ping_aic:logs_pull — tenant log events for a source and time window (begin, end, filter, transaction_id) to a newline-delimited JSON output_file, masked by default: the evidence for an incident change.
  • ping_aic:api — the escape hatch, gated harder than the typed actions (below).
  • ping_aic:ensure — pre-warm the pinned pingaic binary on a runner (source, version, url, sha256; publishes pingaic_path, pingaic_version). Never a precondition — see below.

Every step resolves the pinned pingaic binary itself (PATH, then the runner cache, then the pack's own bundled tools/ directory, then a sha256-verified download — never an unpinned public URL). The ensure step each action carries, and ping_aic:ensure on its own, pre-warm a runner; neither is a precondition: the platform dispatches step by step, so sibling steps of one action may execute on different runners of a group, and a step never trusts a path a sibling published. With no channel available a step refuses by name, pointing at ping_aic:ensure and the signed bundle. A pack installed from its signed bundle carries the tool, so a fresh runner needs no toolchain and no download configuration.

The gated escape hatch​

ping_aic:api dials one tenant-relative REST endpoint (method, path with {realm} substituted from realm, body, query, api_version). It refuses, with exit code 78, unless two independent gates are open:

  1. the tenant document declares api_lane: enabled — the configuration repository's owners opted the tenant in, under review;
  2. the step environment carries PING_AIC_API_LANE=enabled — an opschain.env property the estate's administrators set on the OpsChain environment, so path-based authorisation governs who may grant it and an approval policy can sit on the environment.

The pack entitlement applies on top, and the step log announces every dial through the lane. Prefer a typed action; a gap worth this lane is a gap worth reporting.

Worked examples​

Each example is a complete declarative action file (.opschain/actions/<name>.yaml) composing the pack's actions with uses: + with: (opschain docs orchestration). Nothing secret appears in any of them — credential custody is server-side — and tenant_file is omitted wherever the environment's ping_aic.tenant_file property should carry it.

The pack ships one such action per resource kind under examples/ping/actions/ — reconcile_esvs (apply, restart when anything changed, list the names in force), publish_scripts, release_journeys (apply, then promote with a dry run first), reconcile_oauth2_clients (apply only when the plan found a difference), reconcile_realm_services, extend_managed_objects (apply, then search the new object's records), reconcile_connectors (the credentials as environment secrets first), reconcile_sync_mappings, reconcile_email_templates (inline values) and reconcile_themes (plan with prune as a report of undeclared themes) — each walked through in its kind's page (the resources index above). The examples below compose across kinds.

Prove the resources are in sync (ping_aic:plan)​

# .opschain/actions/check_drift.yaml
action: check_drift
description: Compare the committed resources with the tenant and keep the plan as evidence
steps:
- name: plan
uses: ping_aic:plan
with: { dir: .opschain/ping/resources, plan_file: drift.txt }
- name: report
run: echo "drift=${{ steps.plan.outputs.has_changes }} (${{ steps.plan.outputs.updates }} updates)"

Reconcile, then promote with the report as evidence​

The approval that governs the promotion into production is the approval policy on the OpsChain environment this change runs under.

# .opschain/actions/release_identity_config.yaml
action: release_identity_config
description: Apply the resources, then promote the environment with a dry run first
steps:
- name: apply
uses: ping_aic:apply
with: { dir: .opschain/ping/resources, prune: "ping_aic_journey,ping_aic_script" }
- name: lock
uses: ping_aic:promotion_lock
- name: rehearse
uses: ping_aic:promotion_promote
with: { dry_run: "true", report_file: promotion-dry-run.json }
- name: promote
uses: ping_aic:promotion_promote
with: { description: "identity config release", report_file: promotion-report.json }
- name: unlock
uses: ping_aic:promotion_unlock
with: { promotion_id: '${{ steps.lock.outputs.promotion_id }}' }

Rotate a secret and apply it (ping_aic:esv_set, ping_aic:restart)​

The secret's value rides an opschain.env property carrying a vault reference; the action names the variable, never the value.

# .opschain/actions/rotate_api_key.yaml
action: rotate_api_key
description: Replace the upstream API key and restart so the tenant picks it up
steps:
- name: set
uses: ping_aic:esv_set
with: { name: esv-api-key, type: secret, value_env: PING_AIC_ESV_API_KEY, description: "upstream API key" }
- name: restart
uses: ping_aic:restart
with: { wait_timeout: 20m }

Bulk identities, masked (ping_aic:identity_create)​

# .opschain/actions/onboard_users.yaml
action: onboard_users
description: Create the quarter's new users from the committed file
steps:
- name: create
uses: ping_aic:identity_create
with: { object_type: alpha_user, file: onboarding/2026-q4.ndjson, output_file: onboarding-results.json }
- name: record
run: echo "created ${{ steps.create.outputs.succeeded }} of ${{ steps.create.outputs.count }}"

Adopt an existing configuration tree (ping_aic:adopt)​

# .opschain/actions/adopt_config.yaml
action: adopt_config
description: Convert the tenant configuration tree the team already commits into YAML resources
steps:
- name: adopt
uses: ping_aic:adopt
with: { from: config/aic-dev, to: .opschain/ping/resources }
- name: record
run: echo "adopted ${{ steps.adopt.outputs.documents }} documents; unmapped: ${{ steps.adopt.outputs.unmapped }}"

Exit codes​

64 usage · 65 validation · 68 not found · 73 conflict (a lock already held, a revision mismatch) · 75 throttled (after Retry-After-honouring retries) · 77 authorization · 78 gated · 1 anything else. Workflows branch on the class, not on message scraping.

Entitlement and lapse behaviour​

The signed-pack gate, uniformly: a LimePoint-signed pack whose verified manifest requires packs:ping-aic is gated on that feature at install and at every action run — the refusal names the pack, the mount and the missing entitlement, and lands a customer-visible licence warning event. The grant is layered by a pack-tier licence document beside your base licence and stops contributing once that document expires past its grace window: installed content and stored configuration are never removed, reads stay up, and everything lights back up when a renewed licence is applied.

Where the rest lives​

  • Entitlements — the pack vocabulary and refusal shapes.
  • The pack store — installing signed bundles.
  • Secrets providers — the vault references the service account and ESV values ride.
  • opschain docs getting-started/2-4-using-packs — mounting a pack onto a template.