Pulumi's Node.js SDK for infrastructure-as-code platform that allows you to create, deploy, and manage infrastructure using familiar programming languages and tools.
85
Pulumi's provider development framework enables building custom resource providers for new cloud services and infrastructure platforms, extending Pulumi's ecosystem beyond the built-in providers.
interface Provider {
check?(urn: string, olds: any, news: any, randomSeed?: Buffer): Promise<CheckResult>;
create?(urn: string, inputs: any, timeout?: number, preview?: boolean): Promise<CreateResult>;
read?(id: string, urn: string, props?: any): Promise<ReadResult>;
update?(id: string, urn: string, olds: any, news: any, timeout?: number, ignoreChanges?: string[], preview?: boolean): Promise<UpdateResult>;
delete?(id: string, urn: string, props: any, timeout?: number): Promise<void>;
construct?(name: string, type: string, inputs: any, options: any): Promise<ConstructResult>;
invoke?(token: string, inputs: any, options?: any): Promise<InvokeResult>;
parameterize?(parameters: any): Promise<ParameterizeResult>;
configure?(inputs: any): Promise<void>;
}
function main(provider: Provider, args?: string[]): Promise<void>;interface CheckResult {
inputs?: any;
failures?: CheckFailure[];
}
interface CreateResult {
id: string;
properties?: any;
}
interface ReadResult {
id?: string;
properties?: any;
inputs?: any;
}
interface UpdateResult {
properties?: any;
}
interface ConstructResult {
urn: string;
state?: any;
}
interface InvokeResult {
properties?: any;
failures?: CheckFailure[];
}
interface ParameterizeResult {
name: string;
version: string;
}
interface CheckFailure {
property: string;
reason: string;
}function deserializeInputs(inputsStruct: any, inputDependencies: any): any;
function containsOutputs(input: any): boolean;
function serializeProperties(props: any, keepOutputValues?: boolean): any;
function deserializeProperties(props: any): any;import * as pulumi from "@pulumi/pulumi";
import * as provider from "@pulumi/pulumi/provider";
interface MyServiceConfig {
apiEndpoint?: string;
apiToken?: string;
}
class MyProvider implements provider.Provider {
private config: MyServiceConfig = {};
async configure(inputs: any): Promise<void> {
this.config = inputs;
}
async check(urn: string, olds: any, news: any): Promise<provider.CheckResult> {
const failures: provider.CheckFailure[] = [];
// Validate required properties
if (!news.name) {
failures.push({ property: "name", reason: "name is required" });
}
return { inputs: news, failures };
}
async create(urn: string, inputs: any): Promise<provider.CreateResult> {
// Create resource via external API
const response = await this.callApi('POST', '/resources', inputs);
return {
id: response.id,
properties: {
...inputs,
status: response.status,
createdAt: response.createdAt,
},
};
}
async read(id: string, urn: string, props?: any): Promise<provider.ReadResult> {
try {
const response = await this.callApi('GET', `/resources/${id}`);
return {
id: response.id,
properties: {
name: response.name,
status: response.status,
createdAt: response.createdAt,
},
};
} catch (error) {
if (error.statusCode === 404) {
return { id: undefined, properties: undefined };
}
throw error;
}
}
async update(id: string, urn: string, olds: any, news: any): Promise<provider.UpdateResult> {
const response = await this.callApi('PUT', `/resources/${id}`, news);
return {
properties: {
...news,
status: response.status,
updatedAt: response.updatedAt,
},
};
}
async delete(id: string, urn: string, props: any): Promise<void> {
await this.callApi('DELETE', `/resources/${id}`);
}
async invoke(token: string, inputs: any): Promise<provider.InvokeResult> {
switch (token) {
case "myservice:index:getResource":
const response = await this.callApi('GET', `/resources/${inputs.id}`);
return { properties: response };
default:
throw new Error(`Unknown invoke token: ${token}`);
}
}
private async callApi(method: string, path: string, body?: any): Promise<any> {
const response = await fetch(`${this.config.apiEndpoint}${path}`, {
method,
headers: {
'Authorization': `Bearer ${this.config.apiToken}`,
'Content-Type': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
});
if (!response.ok) {
throw new Error(`API call failed: ${response.status} ${response.statusText}`);
}
return response.json();
}
}
// Start the provider
provider.main(new MyProvider());import * as pulumi from "@pulumi/pulumi";
import * as provider from "@pulumi/pulumi/provider";
class ComponentProvider implements provider.Provider {
async construct(name: string, type: string, inputs: any, options: any): Promise<provider.ConstructResult> {
switch (type) {
case "myservice:index:WebApp":
return this.constructWebApp(name, inputs, options);
default:
throw new Error(`Unknown component type: ${type}`);
}
}
private async constructWebApp(name: string, inputs: any, options: any): Promise<provider.ConstructResult> {
// Create child resources programmatically
const bucket = new pulumi.aws.s3.Bucket(`${name}-bucket`, {
website: {
indexDocument: "index.html",
},
});
const distribution = new pulumi.aws.cloudfront.Distribution(`${name}-cdn`, {
origins: [{
domainName: bucket.websiteEndpoint,
originId: "S3-Website",
customOriginConfig: {
httpPort: 80,
httpsPort: 443,
originProtocolPolicy: "http-only",
},
}],
defaultCacheBehavior: {
targetOriginId: "S3-Website",
viewerProtocolPolicy: "redirect-to-https",
compress: true,
allowedMethods: ["GET", "HEAD"],
},
enabled: true,
});
// Return component state
return {
urn: `urn:pulumi:${pulumi.getStack()}::${pulumi.getProject()}::${type}::${name}`,
state: {
bucketName: bucket.id,
distributionId: distribution.id,
websiteUrl: pulumi.interpolate`https://${distribution.domainName}`,
},
};
}
}
provider.main(new ComponentProvider());import * as pulumi from "@pulumi/pulumi";
import * as provider from "@pulumi/pulumi/provider";
interface DatabaseResourceInputs {
name: string;
size?: "small" | "medium" | "large";
backupRetention?: number;
multiAZ?: boolean;
}
class DatabaseProvider implements provider.Provider {
async check(urn: string, olds: any, news: DatabaseResourceInputs): Promise<provider.CheckResult> {
const failures: provider.CheckFailure[] = [];
// Validate name
if (!news.name || news.name.length < 3) {
failures.push({
property: "name",
reason: "name must be at least 3 characters long"
});
}
// Validate size
if (news.size && !["small", "medium", "large"].includes(news.size)) {
failures.push({
property: "size",
reason: "size must be one of: small, medium, large"
});
}
// Validate backup retention
if (news.backupRetention !== undefined &&
(news.backupRetention < 0 || news.backupRetention > 35)) {
failures.push({
property: "backupRetention",
reason: "backupRetention must be between 0 and 35 days"
});
}
return { inputs: news, failures };
}
async create(urn: string, inputs: DatabaseResourceInputs): Promise<provider.CreateResult> {
// Map size to instance type
const instanceTypeMap = {
small: "db.t3.micro",
medium: "db.t3.small",
large: "db.t3.medium",
};
const instanceType = instanceTypeMap[inputs.size || "small"];
// Create RDS instance (pseudo-code)
const dbInstance = new pulumi.aws.rds.Instance(`${inputs.name}-db`, {
instanceClass: instanceType,
engine: "postgres",
dbName: inputs.name,
multiAz: inputs.multiAZ || false,
backupRetentionPeriod: inputs.backupRetention || 7,
// ... other properties
});
return {
id: inputs.name,
properties: {
name: inputs.name,
size: inputs.size || "small",
instanceType: instanceType,
endpoint: dbInstance.endpoint,
port: dbInstance.port,
},
};
}
async invoke(token: string, inputs: any): Promise<provider.InvokeResult> {
switch (token) {
case "mydb:index:getAvailableVersions":
return {
properties: {
versions: ["13.7", "14.6", "15.2"]
}
};
case "mydb:index:validateName":
const isValid = /^[a-zA-Z][a-zA-Z0-9_]*$/.test(inputs.name);
return {
properties: {
valid: isValid,
message: isValid ? "Name is valid" : "Name must start with letter and contain only alphanumeric characters and underscores"
}
};
default:
throw new Error(`Unknown invoke: ${token}`);
}
}
}
provider.main(new DatabaseProvider());import * as pulumi from "@pulumi/pulumi";
import * as provider from "@pulumi/pulumi/provider";
interface ProviderConfig {
endpoint: string;
apiKey: string;
region?: string;
timeout?: number;
}
class ConfigurableProvider implements provider.Provider {
private config: ProviderConfig;
async configure(inputs: any): Promise<void> {
// Validate configuration
if (!inputs.endpoint) {
throw new Error("endpoint is required");
}
if (!inputs.apiKey) {
throw new Error("apiKey is required");
}
this.config = {
endpoint: inputs.endpoint,
apiKey: inputs.apiKey,
region: inputs.region || "us-east-1",
timeout: inputs.timeout || 30000,
};
// Test connection
await this.testConnection();
}
private async testConnection(): Promise<void> {
try {
const response = await fetch(`${this.config.endpoint}/health`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${this.config.apiKey}`,
},
signal: AbortSignal.timeout(this.config.timeout!),
});
if (!response.ok) {
throw new Error(`Health check failed: ${response.status}`);
}
} catch (error) {
throw new Error(`Failed to connect to ${this.config.endpoint}: ${error.message}`);
}
}
async check(urn: string, olds: any, news: any): Promise<provider.CheckResult> {
// Implementation...
return { inputs: news, failures: [] };
}
// Other provider methods...
}
provider.main(new ConfigurableProvider());check methodInstall with Tessl CLI
npx tessl i tessl/npm-pulumi--pulumidocs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10