Files
netbird-iac/netbird.ts
Prox feb959703f
Some checks failed
Pulumi / pulumi (push) Failing after 1m17s
Initial Pulumi Typescript implementation
2026-02-15 17:16:47 +02:00

245 lines
7.3 KiB
TypeScript

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<string>;
}
export interface GroupArgs {
name: string;
peers?: string[];
}
export interface PolicyRuleArgs {
name: string;
description?: string;
enabled: boolean;
sources: pulumi.Input<string>[];
destinations: pulumi.Input<string>[];
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<string>[];
usageLimit: number;
expiresIn: number;
ephemeral: boolean;
}
// =============================================================================
// NetBird Group Resource
// =============================================================================
export class Group extends pulumi.ComponentResource {
public readonly id: pulumi.Output<string>;
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<string>;
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<string>;
public readonly key: pulumi.Output<string>;
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,
});
}
}