> ## Documentation Index
> Fetch the complete documentation index at: https://ryvn.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Pulumi (beta)

> Deploy Go infrastructure programs with managed state, secrets, approvals, and Helm consumers.

Pulumi services provision infrastructure from Go programs in your GitHub repository. The environment's agent runs each installation's stack; Ryvn manages state, configuration, approvals, and outputs. No Pulumi Cloud account is needed.

> [!NOTE]
>
> Ask Ryvn to enable the beta for your organization.

## Prerequisites

* A provisioned target environment ([setup](#environment-setup)).
* A connected, Pulumi-capable agent with resource permissions and outbound access for Go modules and Pulumi providers.
* Go 1.26.10 locally to build the example.
* A connected GitHub repository, an authenticated [current Ryvn CLI](https://ryvn.ai/docs/cli.md#install) ([update instructions](https://ryvn.ai/docs/cli.md#upgrade)), and an existing `stable` release channel.
* For Helm, a `web` service with a release in `stable` and a chart accepting `app.deploymentName`.

### Environment setup

The beta currently supports GCP environments. Ask your environment owner to set `managedStateBackend: true` in [platform inputs](https://ryvn.ai/docs/iac/environments/gcp.md#platform). Review all changes in the provisioning plan and complete provisioning before installing Pulumi.

## Deploy a resource

The Random provider persists a generated deployment name in stack state.

**1. Prepare the source**

Create `infrastructure/` in your repository and save these files:

```yaml infrastructure/Pulumi.yaml theme={null}
name: infrastructure
runtime: go
```

```go infrastructure/main.go theme={null}
package main

import (
    "github.com/pulumi/pulumi-random/sdk/v4/go/random"
    "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
)

func main() {
    pulumi.Run(func(ctx *pulumi.Context) error {
        cfg := config.New(ctx, "")
        name, err := random.NewRandomPet(ctx, "deployment", &random.RandomPetArgs{
            Prefix: pulumi.String(cfg.Require("namePrefix")),
            Length: pulumi.Int(2),
        })
        if err != nil {
            return err
        }
        ctx.Export("deploymentName", name.ID())
        return nil
    })
}
```

Initialize and check the module locally:

```sh theme={null}
cd infrastructure
go mod init example.invalid/infrastructure
go get github.com/pulumi/pulumi/sdk/v3@v3.272.0 github.com/pulumi/pulumi-random/sdk/v4@v4.18.2
go mod tidy
go build ./...
```

Commit and push source, `Pulumi.yaml`, `go.mod`, `go.sum`, and required local/generated Go source; exclude build output.

**2. Create the service and release**

Use [Git Sync](https://ryvn.ai/docs/iac/overview.md), replacing the synthetic repository:

```yaml infrastructure.service.yaml theme={null}
# yaml-language-server: $schema=https://api.ryvn.app/v1/schemas/resources.json
kind: Service
metadata:
  name: infrastructure
spec:
  type: pulumi
  repo: example/infrastructure
  projectName: infrastructure
  autoApprove: false
  build:
    path: infrastructure
```

`projectName` must match `Pulumi.yaml`. Both `projectName` and `build.path` are immutable. Pulumi `build` accepts only `path` and `branches`; omit `build` or `build.path` to use the repository root.

Once synced, publish from that checkout:

```sh theme={null}
ryvn create release infrastructure 1.0.0 --channel stable
```

Releases require a full 40-character commit SHA. If CLI Git detection fails, pass `--metadata-file release-metadata.yaml` with your pushed commit:

```yaml release-metadata.yaml theme={null}
commit:
  sha: "0123456789abcdef0123456789abcdef01234567"
  gitSourceType: github
  commitMessage: "Add infrastructure program"
```

Ryvn checks out that SHA, not the version string. The dashboard's manual release form cannot publish Pulumi releases.

**3. Configure the installation**

Set the name prefix and provisioned environment, then sync:

```yaml infrastructure.installation.yaml theme={null}
# yaml-language-server: $schema=https://api.ryvn.app/v1/schemas/resources.json
kind: ServiceInstallation
metadata:
  name: infrastructure
spec:
  service: infrastructure
  environment: staging
  releaseChannel: stable
  config: |
    namePrefix: web
```

`namePrefix` is ordinary project config read by the program. Provider-specific config uses the provider's namespace.

## Secrets

Installation secrets become whole Pulumi secret objects under their names. Variable groups require **Link group** or this optional addition under installation `spec`; the group must already exist:

```yaml theme={null}
variableGroups:
  - name: database
```

Installation secrets override same-name groups. Do not duplicate their names in ordinary config or use `k8sSecretValue` templates.

For a `database` secret containing `username` and `password`, add this inside the `pulumi.Run` callback:

```go theme={null}
type Database struct {
    Username string `json:"username" pulumi:"username"`
    Password string `json:"password" pulumi:"password"`
}
var database Database
credentials := config.RequireSecretObject(ctx, "database", &database)
password := credentials.ApplyT(func(value any) string {
    return value.(*Database).Password
}).(pulumi.StringOutput)
// Pass password to your resource's password input.
```

Use the returned Output to preserve [secret tracking](https://www.pulumi.com/docs/iac/concepts/secrets/), not the decoded struct. Never log secret values.

## Providers and Helm consumers

A [Go Pulumi program](https://www.pulumi.com/docs/iac/languages-sdks/go/) can create cloud prerequisites and Kubernetes bootstrap Jobs before Helm consumers. The Kubernetes provider can use the agent's in-cluster identity and existing RBAC; Ryvn grants no additional permissions. Provider await behavior determines Job completion. Make bootstrap operations retry-safe and avoid shared Pulumi/Helm resource ownership.

The example chart reads `app.deploymentName` as an application deployment label. Pass the exported name to that value, adapting the key to your chart:

```yaml web.installation.yaml theme={null}
# yaml-language-server: $schema=https://api.ryvn.app/v1/schemas/resources.json
kind: ServiceInstallation
metadata:
  name: web
spec:
  service: web
  environment: staging
  releaseChannel: stable
  dependencies:
    - serviceInstallation: infrastructure
  config: |
    app:
      deploymentName: {{ printf "%q" (serviceInstallation "infrastructure").outputs.deploymentName }}
```

Dependencies gate only the first install. Redeploy consumers when outputs change. Secret outputs require authorized access.

## Approvals and troubleshooting

Environment approval policy takes precedence; if unset, `autoApprove` applies. Required approvals use refreshed previews; automatic operations skip this step. Approval binds release, configuration, and operation, not an exact saved plan. Pulumi recalculates at execution; drift can change results.

> [accordion] Source compilation or provider download fails
>
> The beta compiles Go programs on Linux AMD64 agents with Go 1.26.10, Pulumi 3.272.0, `CGO_ENABLED=0`, and read-only module resolution. Pin dependencies and allow module/provider downloads. Repository checkout credentials do not authenticate private modules; preparation hooks, code generation, and Docker builds are not run.
>
> For an existing Automation API application, expose resource declarations through `pulumi.Run` and Pulumi config; Ryvn owns lifecycle and backend selection. Separate unrelated Rust, CGO, or generated dependencies into another module only if the deployment program can compile independently under these constraints. Moving files alone does not establish compatibility. No committed executable or wrapper image is required.

> [accordion] An update is interrupted or stuck
>
> Cancellation requests a graceful stop and waits for process exit; it is not rollback. Involve your operator or Ryvn support for a stuck operation. Confirm the old executor has stopped before recovering a stale lock, then follow [Pulumi's interrupted-update guidance](https://www.pulumi.com/docs/iac/operations/troubleshooting/interrupted-updates/) to reconcile state before retrying. Preserve state storage, the encryption key, and recovery access. Disabling the backend is not recovery.
