A Pulumi provider for managing Lagoon resources as infrastructure-as-code.
Documentation Site | Getting Started | Resource Reference
This provider enables you to manage Lagoon hosting platform resources (projects, environments, variables, deploy targets, notifications, tasks, etc.) using Pulumi, with native SDKs for Python, TypeScript/JavaScript, Go, and .NET/C#.
Status: v0.5.1 — Native Go Provider
| Resource | Description |
|---|---|
Project |
Lagoon projects (applications/sites) |
Environment |
Environments (branch/PR deployments) |
Variable |
Project and environment variables |
DeployTarget |
Kubernetes cluster deploy targets |
DeployTargetConfig |
Branch-pattern routing to deploy targets |
NotificationSlack |
Slack deployment notifications |
NotificationRocketChat |
RocketChat deployment notifications |
NotificationEmail |
Email deployment notifications |
NotificationMicrosoftTeams |
Microsoft Teams deployment notifications |
ProjectNotification |
Link notifications to projects |
Task |
Advanced task definitions (command and image types) |
Group |
Groups for organizing projects and users |
User |
Lagoon users (full CRUD) |
UserGroupAssignment |
User-to-group role assignments |
UserPlatformRole |
Platform-level role assignments |
Route |
API-managed routes with annotations, alternative names, and path routing (requires Lagoon v2.29.0+) |
ProjectAutogeneratedRouteConfig |
Project-level autogenerated route configuration (requires Lagoon v2.29.0+) |
EnvironmentAutogeneratedRouteConfig |
Environment-level autogenerated route configuration (requires Lagoon v2.29.0+) |
pip install pulumi-lagoonnpm install @tag1consulting/pulumi-lagoon
# or
yarn add @tag1consulting/pulumi-lagoongo get github.com/tag1consulting/pulumi-lagoon-provider/sdk/go/lagoondotnet add package Tag1Consulting.LagoonThe table below lists Lagoon versions that have been tested with this provider:
| Lagoon Version | lagoon-core chart | lagoon-remote chart | Notes |
|---|---|---|---|
| v2.31.0 | 1.60.0 | 0.104.1 | Tested (single + multi-cluster) |
| v2.30.0 | 1.59.0 | 0.103.0 | Tested; known Knex migrations bug (fixed in v2.31.0) |
| v2.28.0 | 1.56.0 | 0.96.0 | Tested |
| v2.24.1 | 1.52.0 | 0.90.0 | Tested |
Route resources (Route, ProjectAutogeneratedRouteConfig, EnvironmentAutogeneratedRouteConfig) require Lagoon v2.29.0 or later.
pulumi config set lagoon:apiUrl https://api.lagoon.example.com/graphql
pulumi config set --secret lagoon:token YOUR_TOKEN
# or use a JWT secret for admin token generation:
pulumi config set --secret lagoon:jwtSecret YOUR_JWT_SECRETOr via environment variables:
export LAGOON_API_URL=https://api.lagoon.example.com/graphql
export LAGOON_TOKEN=YOUR_TOKENimport pulumi
import pulumi_lagoon as lagoon
from pulumi_lagoon import Project, ProjectArgs, Environment, EnvironmentArgs, Variable, VariableArgs
# The submodule path also works: from pulumi_lagoon.lagoon import Project, ...
project = Project("my-site",
ProjectArgs(
name="my-drupal-site",
git_url="git@github.com:org/repo.git",
deploytarget_id=1,
production_environment="main",
branches="^(main|develop|stage)$",
)
)
# The project's deploy key is available as an output for adding to your Git host
pulumi.export("deploy_key", project.public_key)
prod_env = Environment("production",
EnvironmentArgs(
name="main",
project_id=project.lagoon_id,
deploy_type="branch",
deploy_base_ref="main",
environment_type="production",
)
)
db_config = Variable("db-host",
VariableArgs(
name="DATABASE_HOST",
value="mysql.production.example.com",
project_id=project.lagoon_id,
environment_id=prod_env.lagoon_id,
scope="runtime",
)
)
pulumi.export("project_id", project.lagoon_id)
pulumi.export("production_url", prod_env.route)import * as lagoon from "@tag1consulting/pulumi-lagoon";
// Resources are available directly: lagoon.Project, lagoon.Variable, etc.
// The submodule path also works: lagoon.lagoon.Project
const project = new lagoon.Project("my-site", {
name: "my-drupal-site",
gitUrl: "git@github.com:org/repo.git",
deploytargetId: 1,
productionEnvironment: "main",
branches: "^(main|develop|stage)$",
});
export const projectId = project.lagoonId;import (
lagoon "github.com/tag1consulting/pulumi-lagoon-provider/sdk/go/lagoon/lagoon"
)
project, err := lagoon.NewProject(ctx, "my-site", &lagoon.ProjectArgs{
Name: pulumi.String("my-drupal-site"),
GitUrl: pulumi.String("git@github.com:org/repo.git"),
DeploytargetId: pulumi.Int(1),
ProductionEnvironment: pulumi.String("main"),
})using System.Collections.Generic;
using Pulumi;
using Tag1Consulting.Lagoon.Lagoon;
return await Deployment.RunAsync(() =>
{
var project = new Project("my-site", new ProjectArgs
{
Name = "my-drupal-site",
GitUrl = "git@github.com:org/repo.git",
DeploytargetId = 1,
ProductionEnvironment = "main",
Branches = "^(main|develop|stage)$",
});
return new Dictionary<string, object?>
{
["projectId"] = project.LagoonId,
};
});See the examples/ directory for complete examples:
simple-project/— Create Lagoon projects, environments, variables, and notifications via the APIsingle-cluster/— Deploy a complete Lagoon stack to a single Kind clustermulti-cluster/— Production-like deployment with separate prod/nonprod Kind clusters
# Deploy prod + nonprod Kind clusters with full Lagoon stack
make multi-cluster-up
# Verify deployment
make multi-cluster-status
# Tear down
make multi-cluster-downUse pulumi import to bring existing Lagoon resources under Pulumi management:
| Resource | Import ID Format | Example |
|---|---|---|
lagoon:lagoon:Project |
{numeric_id} |
123 |
lagoon:lagoon:DeployTarget |
{numeric_id} |
1 |
lagoon:lagoon:Environment |
{project_id}:{env_name} |
123:main |
lagoon:lagoon:Variable |
{project_id}:{env_id}:{var_name} |
123:456:DATABASE_HOST |
lagoon:lagoon:Variable (project-level) |
{project_id}::{var_name} |
123::API_KEY |
lagoon:lagoon:DeployTargetConfig |
{project_id}:{config_id} |
123:5 |
lagoon:lagoon:NotificationSlack |
{name} |
deploy-alerts |
lagoon:lagoon:NotificationRocketChat |
{name} |
deploy-alerts |
lagoon:lagoon:NotificationEmail |
{name} |
ops-email |
lagoon:lagoon:NotificationMicrosoftTeams |
{name} |
deploy-alerts |
lagoon:lagoon:ProjectNotification |
{project_name}:{type}:{notification_name} |
my-project:slack:deploy-alerts |
lagoon:lagoon:Task |
{numeric_id} |
456 |
lagoon:lagoon:Group |
{name} |
my-team |
lagoon:lagoon:Route |
{project_name}:{domain} |
my-project:example.com |
lagoon:lagoon:ProjectAutogeneratedRouteConfig |
{project_name} |
my-project |
lagoon:lagoon:EnvironmentAutogeneratedRouteConfig |
{project_name}:{env_name} |
my-project:main |
lagoon:lagoon:User |
{email} |
user@example.com |
lagoon:lagoon:UserGroupAssignment |
{email}:{group_name} |
user@example.com:my-team |
lagoon:lagoon:UserPlatformRole |
{email}:{role} |
user@example.com:OWNER |
# Import an existing project (ID 123)
pulumi import lagoon:lagoon:Project my-site 123
# Import an environment
pulumi import lagoon:lagoon:Environment prod-env 123:main
# Import a project-level variable
pulumi import lagoon:lagoon:Variable api-key 123::API_KEYUse the Lagoon CLI to find resource IDs:
lagoon list projects
lagoon get project --project my-project- Go 1.26+
- Pulumi CLI
- Docker, Kind, kubectl, jq (for local test clusters)
cd provider
CGO_ENABLED=0 go build ./cmd/pulumi-resource-lagoon/cd provider
CGO_ENABLED=0 go test ./... -count=1make go-build # Build the provider binary
make go-test # Run all Go tests (690+ tests)
make go-schema # Regenerate provider schema
make go-sdk-all # Regenerate all language SDKs
make go-sdk-python # Regenerate Python SDK
make go-sdk-nodejs # Regenerate TypeScript SDK
make go-sdk-go # Regenerate Go SDKpulumi-lagoon-provider/
├── provider/ # Native Go provider
│ ├── cmd/pulumi-resource-lagoon/ # Provider binary entrypoint
│ ├── pkg/client/ # Lagoon GraphQL client
│ ├── pkg/config/ # Provider configuration
│ ├── pkg/resources/ # 18 resource implementations
│ └── schema.json # Pulumi schema
├── sdk/ # Generated multi-language SDKs
│ ├── python/ # Python SDK (PyPI: pulumi-lagoon)
│ ├── nodejs/ # TypeScript SDK (npm: @tag1consulting/pulumi-lagoon)
│ ├── go/lagoon/ # Go SDK
│ └── dotnet/ # .NET/C# SDK (NuGet: Tag1Consulting.Lagoon)
├── examples/
│ ├── simple-project/ # Provider usage example
│ ├── single-cluster/ # Single Kind cluster deployment
│ └── multi-cluster/ # Production-like multi-cluster deployment
├── scripts/ # Shared operational scripts
└── docs/ # Additional documentation
A native Go provider using pulumi-go-provider v1.3.0 with the infer package. The provider communicates with the Lagoon GraphQL API and generates multi-language SDKs from a single schema.
Key properties:
- All sensitive fields (
token,jwtSecret,webhook,value) are marked secret and encrypted in state - All 18 resources implement
Diffwith per-fieldDetailedDiff(Update vs UpdateReplace) - All resources support
pulumi import - JWT token generation is centralized and configurable (
jwtAudienceconfig field)
- "lagoon authentication required" — Set
lagoon:tokenviapulumi config set --secret lagoon:token <TOKEN>or exportLAGOON_TOKEN. - Token expired — Lagoon JWT tokens expire after 1 hour. Generate a fresh token or use
jwtSecretfor automatic renewal. - Self-signed certificates — Set
lagoon:insecuretotruefor development instances with self-signed TLS.
- "not found" on import — Verify the resource exists in Lagoon. Import IDs are case-sensitive. See the import reference table for the correct format.
- Diff on unchanged resources — If
pulumi previewshows unexpected changes after import, ensure all required fields in your code match the Lagoon API values exactly. - Provider version mismatch — Run
pulumi plugin ls | grep lagoonto verify the installed version matches your SDK version.
Contributions, feedback, and bug reports are welcome! See CONTRIBUTING.md for development setup, coding guidelines, and the pull request process.
This project follows a Code of Conduct. By participating, you agree to uphold these standards.
Apache License 2.0 — See LICENSE for details.
- GitHub Issues: Create an issue
- Lagoon Community: RocketChat