Osano Pulumi Provider
⚠️ Unofficial community provider
This repo is not affiliated with Osano or Pulumi. It is maintained by the community and provided "as is" under the MIT license. Use GitHub Issues for support.
The provider lets you manage Osano Cookie Consent and Unified Consent workflows alongside the rest of your infrastructure-as-code. You can:
- Create Cookie Consent configurations and rules, publish them after all dependencies settle, and export the hosted CMP script URL and exact HTML tag, so the same pipeline that provisions a website can put the consent script first in its
<head>. - Look up the script, publish status, rules, discoveries, and audit log of any Cookie Consent configuration with
getCookieConsentConfig,getCookieConsentConfigs,getCookieConsentRules,getCookieConsentDiscoveries, andgetCookieConsentAuditLog, for example to consume a centrally managed configuration from a website stack or to gate a switch to production mode. - Submit consent decisions programmatically from Pulumi deployments, including Global Privacy Control consents.
- Query unified consent state for a subject using Pulumi invokes.
- Resolve verified, anonymous, and session references via
osano.getSubject,osano.getSubjectProfile, andosano.getSessionwhen stitching identity flows. - Inspect UC configuration and privacy protocol collections with
osano.getConfig,osano.getCollections, andosano.getCollectioninvokes. - Check for existing consent state and hashed consent profiles with
osano.checkConsentandosano.getConsentProfile. - Start and verify subject-profile challenges via
osano.sendSubjectCodeandosano.verifySubjectCode. Pulumi runs invokes on every preview, update, and refresh, so call these two from automation rather than declaring them in a long-lived stack; otherwise each run sends a new code. - Wire Osano calls into your CI/CD pipelines with first-class Node.js, Python, Go, .NET, and Java SDKs.
Table of contents
- Prerequisites
- Installation
- Quick start: publish a consent script
- Cookie Consent end to end
- Unified Consent
- Authentication
- Configuration
- Examples
- Development
Prerequisites
- Pulumi CLI v3+
- API access to an Osano tenant (a Customer REST API key for Cookie Consent, a Unified Consent API key for Unified Consent, or both for mixed workloads)
- Runtime for your preferred language: Node.js 22+ (required by current
@pulumi/pulumireleases), Python 3.10+, Go 1.26.6+, .NET 8+, or Java 11+
Installation
Add the SDK for your language to a Pulumi program. Each package is published to its language's registry; the badges above show the latest version:
-
Node.js:
@jflavan/pulumi-osanoon npm:npm install @jflavan/pulumi-osano -
Python:
pulumi-osanoon PyPI, imported aspulumi_osano:pip install pulumi-osano -
Go:
github.com/jflavan/pulumi-osano/sdk/go/osano:go get github.com/jflavan/pulumi-osano/sdk/go/osano -
.NET:
Community.Pulumi.Osanoon NuGet:dotnet add package Community.Pulumi.Osano -
Java:
io.github.jflavan.pulumi:pulumi-osanoon Maven Central. With Gradle,implementation("io.github.jflavan.pulumi:pulumi-osano:0.2.0"); with Maven:<dependency> <groupId>io.github.jflavan.pulumi</groupId> <artifactId>pulumi-osano</artifactId> <version>0.2.0</version> </dependency>
The SDK declares its provider plugin, and Pulumi downloads the matching pulumi-resource-osano release from GitHub the first time you run pulumi preview or pulumi up. To install it manually, pin the version and point Pulumi at the GitHub releases:
pulumi plugin install resource osano 0.2.0 --server github://api.github.com/jflavan/pulumi-osano
The Java and plugin commands pin 0.2.0, the current release. Package publishing lists every published artifact, how each one is released, and how to verify its provenance or signature.
Quick start: publish a consent script
This TypeScript program creates a Cookie Consent configuration with one rule, publishes it, and hands the script tag to the rest of the program. It assumes a project created with pulumi new typescript:
npm install @jflavan/pulumi-osano
pulumi config set osano:osanoApiKey --secret # Customer REST API key
pulumi up
index.ts:
import { createHash } from "crypto";
import * as pulumi from "@pulumi/pulumi";
import * as osano from "@jflavan/pulumi-osano";
const desired = {
name: "www-example-com",
domains: ["www.example.com"],
mode: "permissive",
configuration: { storagePolicyHref: "https://www.example.com/privacy" },
rules: [{ storeType: "cookies", classification: "ANALYTICS", rule: "_ga", ruleType: "EXACT_MATCH" }],
};
const config = new osano.CookieConsentConfig("consent", {
name: desired.name,
domains: desired.domains,
mode: desired.mode,
configuration: desired.configuration,
});
const rules = desired.rules.map((rule, i) =>
new osano.CookieConsentRule(`rule-${i}`, { configId: config.configId, ...rule }));
// Publish exactly once per change: derive the token from everything that is published.
const publication = new osano.CookieConsentPublication("publication", {
configId: config.configId,
changeToken: createHash("sha256").update(JSON.stringify(desired)).digest("hex"),
}, { dependsOn: [config, ...rules], customTimeouts: { create: "20m", update: "20m" } });
// Hand the tag to whatever renders or configures the site's <head>; it must come first.
export const scriptTag = publication.scriptTag;
export const headHtml = pulumi.interpolate`<head>\n ${publication.scriptTag}\n</head>`;
pulumi preview never publishes. pulumi up creates the configuration and rule, publishes, waits for Osano to finish, and returns <script src="https://cmp.osano.com/{customerId}/{configId}/osano.js"></script>. Running it again without changes publishes nothing.
If you're working from a repository clone instead of published packages, the repo-local examples under examples are aimed at contributors: they build against the SDKs generated in the clone and need a locally built provider plugin. Follow the setup in the quickstart README, which also shows how to switch an example to the published packages.
Cookie Consent end to end
The canonical C# Cookie Consent example creates a CMP configuration and its rules, then uses CookieConsentPublication to publish only after those resources settle. The companion TypeScript example implements the same lifecycle. Both compute a deterministic changeToken, declare explicit dependsOn relationships, and allow a twenty-minute customTimeouts window.
Cookie Consent resources require a Customer REST API key:
export OSANO_API_KEY="replace-with-a-customer-rest-api-key"
After publication succeeds, the resource exposes these exact public outputs:
var publication = new CookieConsentPublication(/* ... */);
return new Dictionary<string, object?>
{
["cookieConsentScriptSrc"] = publication.ScriptSrc, // scriptSrc
["cookieConsentScriptTag"] = publication.ScriptTag, // scriptTag
};
scriptSrc has the form https://cmp.osano.com/{customerId}/{configId}/osano.js; scriptTag is exactly <script src="{scriptSrc}"></script>. These installation values are deliberately non-secret. Put the returned tag first in the site <head> without async or defer, so the CMP loads before scripts it may control. The URL never changes between revisions, so a website only needs it once. Publication completion and CDN propagation are separate: Osano's CDN can take up to 15 minutes to serve a new revision, and browsers cache osano.js for up to 24 hours.
To use the script in another stack, such as one per website, read it with getCookieConsentConfig instead of managing the configuration there:
const consent = osano.getCookieConsentConfigOutput({ configId: "<config-id>" });
export const headScript = consent.scriptTag; // the same value the publication exports
export const published = consent.publishStatus; // the URL returns 403 until the first publish
Before switching a configuration to production mode, which blocks everything unclassified, getCookieConsentDiscoveries lists what osano.js has discovered that no rule covers yet. The end-to-end workflow guide covers the whole pipeline, including Content Security Policy settings and per-environment configurations.
See Osano's Consent JavaScript API and direct Customer REST API publishConfig operation for the upstream contracts.
Unified Consent
The Consent resource submits a consent decision, and the functions read consent state back. This TypeScript snippet assumes a project created with pulumi new typescript:
npm install @jflavan/pulumi-osano
pulumi config set osano:unifiedConsentApiKey --secret
pulumi config set subjectRef <subject-id> --secret
pulumi config set configId <config-id>
pulumi config set privacyProtocolId <protocol-id>
pulumi up
index.ts:
import * as pulumi from "@pulumi/pulumi";
import * as osano from "@jflavan/pulumi-osano";
const cfg = new pulumi.Config();
const subjectRef = cfg.requireSecret("subjectRef");
const configId = cfg.require("configId");
const privacyProtocolId = cfg.require("privacyProtocolId");
const subjectType = cfg.get("subjectType") ?? "verified";
const subject = subjectRef.apply((value) =>
subjectType === "anonymous" ? { anonymousId: value } : { verifiedId: value }
);
const consent = new osano.Consent("example", {
subject,
actions: [
{ target: privacyProtocolId, vendor: configId, action: "ACCEPT" },
],
attributes: { pulumiStack: pulumi.getStack() },
origin: "api",
tags: ["demo"],
});
export const consentId = consent.consentId;
Run pulumi up to submit the consent. Destroying the stack removes the logical Pulumi resource but does not delete historical events from Osano (they are immutable).
Look anonymous and verified IDs up with the default referenceType (subject); session resolves a session ID. To submit a Global Privacy Control consent, set origin: "gpc" and omit actions: Osano derives the actions and the resource exports them as gpcActions. When a pipeline submits consents on a subject's behalf, set countryCodeOverride (and regionCodeOverride) so Osano does not geolocate the CI runner. The runnable version of this program is in examples/quickstart.
Authentication
Two API keys exist:
| Key | Header | Usage |
|---|---|---|
| Unified Consent API key | x-uc-api-key |
Required for consent submissions and read operations |
| Osano Customer REST API key | x-osano-api-key |
Required for Cookie Consent resources and functions; sendSubjectCode and verifySubjectCode send every configured key, so either this key or the Unified Consent API key is enough |
Configure them with Pulumi config:
pulumi config set osano:unifiedConsentApiKey --secret
pulumi config set osano:osanoApiKey --secret # required for Cookie Consent and subject verification
Or set environment variables for CI:
export OSANO_UC_API_KEY="..."
export OSANO_API_KEY="..."
Configuration
Provider-level settings (all optional unless noted):
| Key | Description |
|---|---|
osano:unifiedConsentApiKey |
Unified Consent API key (secret); OSANO_UC_API_KEY takes precedence when set |
osano:osanoApiKey |
Customer REST API key for Cookie Consent and subject verification (secret); OSANO_API_KEY takes precedence when set |
osano:apiBaseUrl |
Override the Unified Consent API base URL, including any path prefix; defaults to https://uc.api.osano.com; OSANO_API_BASE_URL takes precedence when set |
osano:customerBaseUrl |
Override the Customer REST API base URL; defaults to https://api.osano.com |
osano:requestTimeoutSeconds |
HTTP timeout for Customer REST and Unified Consent calls, default 60 seconds; OSANO_API_TIMEOUT_SECONDS takes precedence when valid |
The deprecated osano:ucApiKey and osano:ucBaseUrl keys are still read as fallbacks for unifiedConsentApiKey and apiBaseUrl.
Resource-level inputs are documented in the generated SDKs, for example the Go package reference.
Examples
- examples/quickstart/typescript
- examples/quickstart/python
- examples/quickstart/go
- examples/cookie-consent (canonical C# and companion TypeScript)
These repo-local examples contain Pulumi.yaml plus language-specific dependency files that reference the SDKs generated in this repository. The shared quickstart README documents the local SDK and plugin setup required when running them from a clone, and how to switch an example to the published packages.
Development
- Install toolchain dependencies:
eval "$(mise activate zsh)" && mise install - Build the provider:
make provider - Run tests:
make test_provider - Regenerate schema + SDKs after editing Go code:
make codegen
For the full lifecycle (install, deploy, day-2 changes, import, teardown, and the contributor loop) see the end-to-end workflow guide. For releases, see the release guide and package publishing. See CONTRIBUTING.md and the docs for troubleshooting tips and workflows.
Release files for pulumi-osano 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pulumi_osano-0.2.0.tar.gz | 43.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pulumi_osano-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 103.9 kB
Release files / pulumi_osano-0.2.0.tar.gz
| Download URL | pulumi_osano-0.2.0.tar.gz |
|---|---|
| Size | 43.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
381aa1eafd9e896b6497ebdd76b8655cc2edad8164c854e4ea1655286af17d18
|
|
BLAKE2b-256 checksum How to use checksums |
c2f45498280a8516fc15fc29c4ac884b10890b1cd4d46679ad8a16338ac043c2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.
Transparency logRelease files / pulumi_osano-0.2.0-py3-none-any.whl
| Download URL | pulumi_osano-0.2.0-py3-none-any.whl |
|---|---|
| Size | 60.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3f8dc33f32b14cba5944e282e243470197b65d3ccada7fd0a6169bf3e760a821
|
|
BLAKE2b-256 checksum How to use checksums |
c255e14b25d3e47b1c3fe17f9491a9fbaccf286fbd975e2c55fb0d42918e6a8d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.
Transparency log