import * as pulumi from "@pulumi/pulumi"; import * as command from "@pulumi/command"; // ============================================================================= // NetBird API Client using Pulumi Command Provider // ============================================================================= // Since there's no TypeScript SDK for NetBird, we use the command provider // to make API calls. This demonstrates the pattern while being practical. export interface NetBirdConfig { url: string; token: pulumi.Output; } export interface GroupArgs { name: string; peers?: string[]; } export interface PolicyRuleArgs { name: string; description?: string; enabled: boolean; sources: pulumi.Input[]; destinations: pulumi.Input[]; bidirectional: boolean; protocol: string; action: "accept" | "drop"; } export interface PolicyArgs { name: string; description?: string; enabled: boolean; rules: PolicyRuleArgs[]; } export interface SetupKeyArgs { name: string; type: "one-off" | "reusable"; autoGroups: pulumi.Input[]; usageLimit: number; expiresIn: number; ephemeral: boolean; } // ============================================================================= // NetBird Group Resource // ============================================================================= export class Group extends pulumi.ComponentResource { public readonly id: pulumi.Output; public readonly name: string; constructor( name: string, args: GroupArgs, config: NetBirdConfig, opts?: pulumi.ComponentResourceOptions ) { super("netbird:custom:Group", name, {}, opts); const createCmd = new command.local.Command( `${name}-create`, { create: pulumi.interpolate`curl -s -X POST \ -H "Authorization: Token ${config.token}" \ -H "Content-Type: application/json" \ -d '{"name": "${args.name}", "peers": ${JSON.stringify(args.peers || [])}}' \ ${config.url}/api/groups`, delete: pulumi.interpolate`GROUP_ID=$(curl -s -H "Authorization: Token ${config.token}" \ ${config.url}/api/groups | jq -r '.[] | select(.name=="${args.name}") | .id') && \ curl -s -X DELETE -H "Authorization: Token ${config.token}" \ ${config.url}/api/groups/$GROUP_ID`, }, { parent: this } ); this.id = createCmd.stdout.apply((stdout): string => { try { const result = JSON.parse(stdout); return result.id || ""; } catch { return ""; } }); this.name = args.name; this.registerOutputs({ id: this.id, name: this.name, }); } } // ============================================================================= // NetBird Policy Resource // ============================================================================= export class Policy extends pulumi.ComponentResource { public readonly id: pulumi.Output; public readonly name: string; constructor( name: string, args: PolicyArgs, config: NetBirdConfig, opts?: pulumi.ComponentResourceOptions ) { super("netbird:custom:Policy", name, {}, opts); // Build the policy payload const rules = args.rules.map((rule) => ({ name: rule.name, description: rule.description || "", enabled: rule.enabled, sources: rule.sources, destinations: rule.destinations, bidirectional: rule.bidirectional, protocol: rule.protocol, action: rule.action, })); const createCmd = new command.local.Command( `${name}-create`, { create: pulumi.all([config.token, ...args.rules.flatMap(r => [...r.sources, ...r.destinations])]).apply( ([token, ...groupIds]) => { const payload = { name: args.name, description: args.description || "", enabled: args.enabled, rules: args.rules.map((rule, i) => ({ name: rule.name, description: rule.description || "", enabled: rule.enabled, sources: rule.sources, destinations: rule.destinations, bidirectional: rule.bidirectional, protocol: rule.protocol, action: rule.action, })), }; return `curl -s -X POST \ -H "Authorization: Token ${token}" \ -H "Content-Type: application/json" \ -d '${JSON.stringify(payload)}' \ ${config.url}/api/policies`; } ), delete: pulumi.interpolate`POLICY_ID=$(curl -s -H "Authorization: Token ${config.token}" \ ${config.url}/api/policies | jq -r '.[] | select(.name=="${args.name}") | .id') && \ curl -s -X DELETE -H "Authorization: Token ${config.token}" \ ${config.url}/api/policies/$POLICY_ID`, }, { parent: this } ); this.id = createCmd.stdout.apply((stdout): string => { try { const result = JSON.parse(stdout); return result.id || ""; } catch { return ""; } }); this.name = args.name; this.registerOutputs({ id: this.id, name: this.name, }); } } // ============================================================================= // NetBird Setup Key Resource // ============================================================================= export class SetupKey extends pulumi.ComponentResource { public readonly id: pulumi.Output; public readonly key: pulumi.Output; public readonly name: string; constructor( name: string, args: SetupKeyArgs, config: NetBirdConfig, opts?: pulumi.ComponentResourceOptions ) { super("netbird:custom:SetupKey", name, {}, opts); const createCmd = new command.local.Command( `${name}-create`, { create: pulumi.all([config.token, ...args.autoGroups]).apply( ([token, ...groupIds]) => { const payload = { name: args.name, type: args.type, auto_groups: groupIds, usage_limit: args.usageLimit, expires_in: args.expiresIn, ephemeral: args.ephemeral, }; return `curl -s -X POST \ -H "Authorization: Token ${token}" \ -H "Content-Type: application/json" \ -d '${JSON.stringify(payload)}' \ ${config.url}/api/setup-keys`; } ), delete: pulumi.interpolate`KEY_ID=$(curl -s -H "Authorization: Token ${config.token}" \ ${config.url}/api/setup-keys | jq -r '.[] | select(.name=="${args.name}") | .id') && \ curl -s -X DELETE -H "Authorization: Token ${config.token}" \ ${config.url}/api/setup-keys/$KEY_ID`, }, { parent: this } ); this.id = createCmd.stdout.apply((stdout): string => { try { const result = JSON.parse(stdout); return result.id || ""; } catch { return ""; } }); this.key = createCmd.stdout.apply((stdout): string => { try { const result = JSON.parse(stdout); return result.key || ""; } catch { return ""; } }); this.name = args.name; this.registerOutputs({ id: this.id, key: this.key, name: this.name, }); } }