Skip to content
Docs BETA

The provisioners for Azure DevOps and Cloud Foundry

Goal

For platform teams. In this task you learn what the two provisioners for real infrastructure do, you decide which one you need, and you configure and register it. Both run as separate services that you deploy.

Before you start

This is not available yet. Joomiz has no download for these two provisioners. Ask the team that gives you the Joomiz platform for the images and for the exact names of the settings in your version.

Steps

Choose the provisioner

Azure DevOps pipeline provisioner Cloud Foundry app provisioner
What it does Starts a pipeline that already exists, and waits for the pipeline to report the result. Creates or updates a pipeline definition for one application. It never starts a run.
How the calls work Slow work. It answers 202, and the pipeline calls it back. Direct. It answers each call at once.
Types that it handles Any pairs that you map to pipelines in its settings. Only deployment and cloud-foundry-app.
Outputs The pipeline reports them in the callback. The ID and the address of the pipeline definition.

The two provisioners do different jobs. The Cloud Foundry app provisioner does not create a Cloud Foundry organization or space. A deployment of the type cloud-foundry-app names a target of the type cloud-foundry-space, and that target needs a provisioner too. You can serve it with a pipeline that you write, behind the Azure DevOps pipeline provisioner.

Set up the Azure DevOps pipeline provisioner

This provisioner never creates infrastructure itself. A pipeline that you write does the real work, for example a terraform apply. The provisioner starts the right pipeline run with the right parameters and reports what the pipeline says.

  1. Say which organization and project to use. Run one deployment of the provisioner for each organization and project.
azure:
  devops:
    organization: my-org
    project: my-project
  1. Give it an identity for Azure DevOps. It signs in as a Microsoft Entra ID application. Set azure.devops.credential.tenant-id and azure.devops.credential.client-id. Then set exactly one of client-secret or certificate. The provisioner does not start with both or with neither. Read the secret or the certificate password from your secret store.

  2. Say how to accept the callback of a pipeline. Set azure.devops.callback.jwks-uri to the signing keys of your tenant. Set azure.devops.callback.required-claims to the exact values of the claims iss and aud of the token that your pipeline sends. Add sub to accept only the service connection of your pipeline. A callback is accepted only when its token matches every claim that you set, and names an operation that is still waiting.

  3. Map each pair to a pipeline. Use the pipeline catalog:

azure:
  devops:
    pipelines:
      - kind: managed-resource
        type: postgres
        pipeline-id: 42
      - kind: target
        type: kubernetes-namespace
        plan-pipeline-id: 10
        apply-pipeline-id: 11

An entry has either pipeline-id, or both plan-pipeline-id and apply-pipeline-id. With one pipeline, the parameter mode tells the pipeline if it runs plan or apply. With two pipelines, no mode is sent. Two entries for the same pair stop the start.

  1. Write the pipeline. Each run receives these parameters:
Parameter Value
resourceId The id of the resource in the change.
environment The environment of the change.
action created, updated or removed.
operationId The ID that the callback must use in its address.
mode plan or apply. It is sent only with one pipeline for both.

Each field of the spec of the resource also becomes a parameter. The name changes from kebab-case to camelCase, so storage-gb becomes storageGb. A field that has the name of a reserved parameter, or that gives the same name as another field, fails the change.

  1. At the end of the run, make the pipeline send a POST to /callbacks/{operationId} of the provisioner. The body is JSON:
{
  "outcome": "succeeded",
  "description": "Provisioned the postgres-01 database.",
  "outputs": {
    "password": "https://vault.example.com/secrets/postgres-01-password"
  }
}

The fields outcome and description are required. The provisioner treats every value of outcome other than the exact text succeeded as a failure. The field outputs is optional. The control plane checks the outputs against the catalog entry of the type. A password or a key is never an output value. Report the https:// address of the vault where the pipeline stored it.

If a callback never arrives, the control plane stops waiting when the longest wait that you registered has passed. It then fails the work. The longest wait is at most 30 minutes. Support slow work in a provisioner explains it.

The provisioner keeps the state of its operations in memory. A restart loses them, and the control plane then fails the work that was waiting.

The setting azure.devops.operations.max-tracked limits the operations that the provisioner holds. The default is 1024. When the limit is reached, a new call fails at once with a clear reason. Do not restart the provisioner while work is waiting.

Set up the Cloud Foundry app provisioner

For each deployment of the type cloud-foundry-app, this provisioner creates or updates one Azure DevOps pipeline definition. The definition points at the shared pipeline template of your platform team. The pipeline runs when someone pushes to the default branch of the application. That run builds the application and pushes it to Cloud Foundry.

When a deployment is removed, the provisioner deletes the pipeline definition. A definition that is already gone is a success.

Before it changes a definition, the provisioner checks that the organization and the space exist in the foundation. It only reads. If either does not exist, the change fails and nothing is created.

  1. Say which Azure DevOps organization and project hold the definitions:
cloud-foundry:
  pipeline:
    organization: my-org
    project: my-project
  1. Say where the shared template is. Set repository-id with the ID that Azure DevOps gives to the place where the template lives. The ID stays the same when someone renames that place:
cloud-foundry:
  pipeline:
    template:
      repository-id: <the ID of the template>
      repository-name: pipeline-templates
      branch: refs/heads/main
      yaml-path: cloud-foundry-app.yml
  1. Say how to name and file the definitions. The agent-pool-queue-id is required. The folder and the prefix have defaults:
cloud-foundry:
  pipeline:
    definition:
      folder: \Joomiz
      agent-pool-queue-id: 12
      name-prefix: joomiz
  1. Give it one identity for each system. Use two separate credentials:
Settings System What the credential may do
cloud-foundry.pipeline.credential.* Azure DevOps Create and edit pipeline definitions. It is a Microsoft Entra ID application. Set tenant-id, client-id and one of client-secret or certificate.
cloud-foundry.foundation.credential.* Cloud Foundry List organizations and spaces. It is a client of the Cloud Foundry UAA (User Account and Authentication service). Set token-url, client-id and client-secret.
  1. Say which foundation it serves, with cloud-foundry.foundation.api-endpoint. One deployment serves one foundation. Run one deployment for each foundation. A deployment that targets another foundation is refused.

If the UAA client cannot see an organization, the provisioner reports it as missing, and a valid deployment fails. Give the client a read-only role that sees the organizations that your deployments use.

Register the provisioner

Register each pair that the provisioner handles, as Register a provisioner shows.

Check

  1. Start the provisioner. A start with a missing or contradictory setting fails, and the log names the setting.
  2. Register it. The registration answers 201.
  3. Run joomiz plan for a file that uses one of its types. The plan shows the answer of the provisioner.
  4. For the Azure DevOps pipeline provisioner, run joomiz apply, and follow the run with joomiz runs watch. The run is waiting for provisioner until the callback arrives.
  5. For the Cloud Foundry app provisioner, look in Azure DevOps. The definition is in the folder that you set, and its name starts with the prefix.

Troubleshooting

Symptom Cause Fix
The provisioner does not start A required setting is missing, or two settings contradict each other. Read the first error in the log. It names the setting.
A call fails with a list of the pairs that the provisioner serves The pair is not in the pipeline catalog. Add the pair to azure.devops.pipelines, or check the spelling.
A pipeline finished, but the run stays waiting for provisioner The callback did not arrive, or the provisioner refused it. Check the callback step of the pipeline, and the claims in required-claims.
A change fails because of a spec field The field has the name of a reserved parameter. Rename the field in the spec.
The Cloud Foundry app provisioner says that an organization or a space is missing The name is wrong, or the UAA client cannot see it. Check the names. Give the client a read-only role that sees them.
The change is refused because of the foundation The target points at another foundation. Use a target of the foundation that this deployment serves.

Next

Learn which types exist, and how a new type arrives, in Own the catalog of types.