Deploy a serverless React + Postgres blueprint on Azure with Pulumi

Switch variant

Choose a different cloud.

Ship a React SPA plus a serverless API that queries Azure Database for PostgreSQL Flexible Server behind Azure Front Door Standard on Azure. Consumes the Pulumi landing-zone stack for network and secret wiring, and exports the public site URL downstream projects can reuse.

Before you deploy: deploy the Azure landing zone first.

This blueprint consumes shared network, identity, and secret-store outputs from the Azure landing-zone stack in the same cloud account. If you haven't deployed one yet, follow Build a Azure landing zone and come back with the stack name.

Download blueprint

Get this Azure blueprint project as a zip. Switch Pulumi language here to keep the download aligned with the install commands and blueprint program on the page.

Download the TypeScript blueprint with the matching Pulumi program, dependency files, and README.

Download TypeScript blueprint

Download the Python blueprint with the matching Pulumi program, dependency files, and README.

Download Python blueprint

Download the Go blueprint with the matching Pulumi program, dependency files, and README.

Download Go blueprint

This blueprint ships a full-stack serverless application on Azure: a React single-page app served from a CDN, a single serverless function that runs a Postgres query, and a managed PostgreSQL database. One pulumi up provisions everything and returns a URL you can open in a browser.

The blueprint covers:

  • A static React + Vite bundle served from Azure Front Door Standard
  • A Azure Functions (Flex Consumption) function with one route, GET /api/random, that runs SELECT floor(random()*100)::int AS n against Azure Database for PostgreSQL Flexible Server
  • a single Front Door endpoint with two origin groups - the Storage static-website origin for /* and the Function origin for /api/* so the browser stays on a single origin and never sees CORS
  • Database credentials generated during pulumi up and stored in Azure Key Vault - injected into the Function through a Key Vault reference (@Microsoft.KeyVault(SecretUri=...))
  • Private networking: the database runs on Azure Functions VNet integration into the landing-zone subnet, pointing at the Flexible Server’s private endpoint attached to the landing-zone private subnets, never exposed to the public internet

The blueprint consumes the Pulumi landing-zone stack through a StackReference so every downstream project reuses the same network and secret conventions.

Architecture on Azure

The Pulumi program is split into two reusable components plus an entrypoint that wires them together:

  • Database - provisions Azure Database for PostgreSQL Flexible Server at the Burstable Standard_B1ms with a private endpoint tier, attaches it to the landing-zone private network, generates a random.RandomPassword, and stores the DB URL in Azure Key Vault.
  • Edge - creates the bucket for the SPA, the Azure Functions (Flex Consumption) that runs the API handler, and Azure Front Door Standard with a single Front Door endpoint with two origin groups - the Storage static-website origin for /* and the Function origin for /api/*. The entrypoint wires these together and exports the public site URL.

The function runs Node.js 20 and ships as a bundled handler.js (esbuild) that imports pg and queries the database. The SPA is a plain Vite + React project with one page and one fetch("/api/random") call; because the API is same-origin, there is no CORS setup and no bearer token in the browser.

Scaling: Flex Consumption scales instances to zero between requests. Flexible Server has no auto-pause yet; the blueprint picks the smallest Burstable tier and the cost + cleanup section shows how to stop the server manually.

The Azure variant uses Azure Functions on the Flex Consumption plan for scale-to-zero compute, Azure Database for PostgreSQL Flexible Server on the Burstable Standard_B1ms tier with a private endpoint into the landing-zone VNet, Key Vault for the DB password (referenced from the Function App with @Microsoft.KeyVault(SecretUri=...)), and Azure Front Door Standard for same-origin CDN routing.

Prerequisites

  • Pulumi account and CLI
  • Node.js 20 or newer and npm
  • an Azure subscription where the Pulumi landing-zone stack is already deployed and you have rights to create Storage accounts, Function Apps, Front Door profiles, Key Vault secrets, PostgreSQL Flexible Servers, and related resources
  • Node.js 20 or newer and npm for building the React SPA and the API bundle (both ship as Node packages regardless of the Pulumi language you pick)
  • A deployed Pulumi landing-zone stack in the same Azure account; see the blueprint prerequisite banner at the top of this page for the link

Landing-zone inputs

The blueprint reads these outputs from your landing-zone/azure stack through pulumi.StackReference:

  • resourceGroupName - the resource group where the blueprint creates its resources
  • networkId - the VNet id that hosts the Flexible Server private endpoint and the Function App VNet integration
  • privateSubnetIds - the subnet ids used for the Function VNet integration and the Flexible Server DNS zone
  • secretsStore - the Key Vault name used to store the DB password secret

If the reference is missing or the output keys are not present, pulumi up fails fast at preview time. Point the stack reference at the right name with:

pulumi config set landingZoneStack <your-org>/landing-zone/<stack>

You do not have to re-deploy the landing-zone stack to iterate on this blueprint. Once the outputs exist, every change here is additive.

Download the blueprint

Use the Download blueprint button at the top of this page to grab the Azure zip for the Pulumi language you selected in the chooser. Each zip contains:

  • index.ts as the Pulumi entrypoint
  • components/database.ts and components/edge.ts as the reusable modules
  • website/ (React + Vite) and api/ (Node handler) as the application code
  • package.json and tsconfig.json for the root Pulumi project

Unzip, change into the directory, and continue with the quickstart below.

Quickstart

Build the SPA and the API bundle, initialize the Pulumi stack, and deploy. The Pulumi program uploads the built artifacts - it does not run the build itself, so you can iterate on the app and redeploy without any Pulumi-side changes.

# 1. Build the React SPA
cd website
npm install
npm run build
cd ..

# 2. Build the API bundle
cd api
npm install
npm run build
cd ..

# 3. Install root Pulumi dependencies
npm install

# 4. Initialize the stack and point it at your landing zone
pulumi stack init dev
pulumi config set azure-native:location eastus
pulumi config set landingZoneStack <your-org>/landing-zone/dev

# 5. Deploy
pulumi up

pulumi up finishes in 5-10 minutes on a cold account, mostly waiting for the database to become available. When it completes, Pulumi prints a siteUrl output; open it in a browser and you should see the SPA showing the random number the API returned from the database.

App walkthrough

The application ships as two Node packages inside the downloadable zip, both independent of the Pulumi language you chose:

  • website/ - a Vite + React project. One page, App.tsx, fetches /api/random and shows the number. There is no client-side router, no state library, and no auth; it is the smallest possible proof that the frontend reaches the backend.
  • api/ - a Node.js 20 TypeScript package. One router in src/handler.ts, one route (GET /api/random), and one pg pool in src/db.ts. esbuild bundles the whole thing to dist/handler.js so the Pulumi program uploads a single file.

The handler

// api/src/handler.ts
import { pool } from "./db";

export async function handle(path: string) {
  if (path === "/api/random") {
    const result = await pool.query<{ n: number }>(
      "SELECT floor(random()*100)::int AS n",
    );
    return { status: 200, body: JSON.stringify({ n: result.rows[0].n }) };
  }
  return { status: 404, body: JSON.stringify({ error: "not found" }) };
}

Adding more API routes is an edit + npm run build + pulumi up cycle. No function-specific glue; the handler is pure TypeScript.

The SPA

// website/src/App.tsx
import { useEffect, useState } from "react";

export default function App() {
  const [n, setN] = useState<number | null>(null);
  useEffect(() => {
    fetch("/api/random")
      .then((r) => r.json())
      .then((data) => setN(data.n));
  }, []);
  return <main>Backend says your lucky number is: {n ?? "…"}</main>;
}

Database and secret wiring

The Database component provisions azure-native:dbforpostgresql:Server at the Burstable Standard_B1ms with a private endpoint tier and a fresh database named after the Pulumi stack. During pulumi up:

  1. Pulumi generates a random.RandomPassword (32 characters, no shell-unsafe symbols).
  2. The password, plus the DB host, port, and database name, is assembled into a Postgres connection string and written to Azure Key Vault (azure-native:keyvault:Secret).
  3. The Azure Functions (Flex Consumption) function is configured so the connection string is injected into the Function through a Key Vault reference (@Microsoft.KeyVault(SecretUri=...)).

The function reads the secret once on cold start and reuses the pg.Pool across invocations. The password never appears in Pulumi stack outputs, never leaves Azure Key Vault in plaintext, and rotates automatically if you change the config.

Database networking: Azure Functions VNet integration into the landing-zone subnet, pointing at the Flexible Server’s private endpoint. The DB endpoint has no public IP, so the function is the only path into it.

Deploy

Run pulumi up once the SPA and API builds are in place. The preview shows three components’ worth of resources: the Database cluster plus secret, the Edge bucket + function + CDN, and the wiring that routes requests between them. Approve the preview and Pulumi deploys the whole stack.

When the deploy finishes, Pulumi prints:

  • siteUrl - the public CDN URL; open it to verify the SPA calls the API and renders the random number
  • apiUrl - the same hostname plus /api, useful when iterating on the SPA locally with npm run dev
  • stack-scoped identifiers (DB name, function name, CDN id) that make log tailing and cache invalidation straightforward

Re-running pulumi up after an app change is fast: Pulumi only updates the function code and re-uploads the SPA, leaving the database and CDN alone.

Stack outputs

Every variant exports the same core outputs so downstream Pulumi projects can consume them with StackReference or Pulumi ESC:

  • siteUrl - the public Azure Front Door Standard URL that serves the SPA
  • apiUrl - the same hostname plus /api, useful for integration tests
  • dbSecretId - the handle to the database secret stored in Azure Key Vault

Cloud-specific outputs on this variant:

  • dbServerName - the Flexible Server resource name for portal navigation and psql connectivity
  • functionAppName - the Function App name so you can tail logs with az webapp log tail
  • frontDoorEndpointHostname - the Front Door endpoint hostname, useful for DNS alias records later

Run pulumi stack output to see the full list with values. Secret-typed outputs require --show-secrets.

Operations and cost

Logs and cache

Function logs stream through Application Insights attached to the Function App. Flexible Server metrics are in the PostgreSQL blade. Front Door request logs and cache invalidations live under the Front Door profile. Clear the SPA cache with az afd endpoint purge --resource-group <rg> --profile-name <profile> --endpoint-name <endpoint> --content-paths "/*" after each npm run build in website/.

Cost

Flex Consumption charges per GB-second of execution, so idle cost for the Function is zero. Flexible Server runs continuously on the Burstable tier; stop it from the portal or with az postgres flexible-server stop when you are not using it. Front Door Standard has a small monthly minimum plus per-request and per-GB charges.

Cleanup

pulumi destroy tears the whole stack down. The object-storage bucket is emptied during destroy so the delete succeeds; if you attached extra objects outside Pulumi, remove them first.

The landing-zone stack is a separate project; destroying this blueprint does not touch it.

Blueprint Pulumi program

The entrypoint reads the landing-zone outputs, creates the database component, then the edge component, and exports the site URL you can open in a browser once pulumi up completes.

import * as pulumi from "@pulumi/pulumi";

import { Database } from "./components/database";
import { Edge } from "./components/edge";

const config = new pulumi.Config();
const landingZoneStackName = config.require("landingZoneStack");
const dbEngineVersion = config.get("dbEngineVersion") ?? "16";
const functionMemoryMB = config.getNumber("functionMemoryMB") ?? 2048;
const websiteDistPath = config.get("websiteDistPath") ?? "./website/dist";
const apiHandlerPath = config.get("apiHandlerPath") ?? "./api/dist";
const location = new pulumi.Config("azure-native").require("location");

const landingZone = new pulumi.StackReference(landingZoneStackName);
const resourceGroupName = landingZone.requireOutput("resourceGroupName") as pulumi.Output<string>;
const vnetId = landingZone.requireOutput("networkId") as pulumi.Output<string>;
const privateSubnetIds = landingZone.requireOutput("privateSubnetIds") as pulumi.Output<string[]>;
const keyVaultName = landingZone.requireOutput("secretsStore") as pulumi.Output<string>;

const projectName = `${pulumi.getStack()}-serverless-react-postgres`;
const commonTags: Record<string, string> = {
    environment: pulumi.getStack(),
    "solution-family": "serverless-react-postgres",
    cloud: "azure",
    language: "typescript",
};

const database = new Database("db", {
    resourceGroupName,
    location,
    vnetId,
    privateSubnetIds,
    keyVaultName,
    engineVersion: dbEngineVersion,
    namePrefix: projectName,
    tags: commonTags,
});

const edge = new Edge("edge", {
    resourceGroupName,
    location,
    vnetId,
    keyVaultName,
    databaseSecretUri: database.secretUri,
    websiteDistPath,
    apiHandlerPath,
    functionMemoryMB,
    namePrefix: projectName,
    tags: commonTags,
});

export const siteUrl = edge.siteUrl;
export const apiUrl = edge.apiUrl;
export const dbSecretId = database.secretUri;
export const dbServerName = database.serverName;
export const functionAppName = edge.functionAppName;
export const frontDoorEndpointHostname = edge.endpointHostname;
export const escEnvironment = `${pulumi.getStack()}-serverless-react-postgres`;
import pulumi

from components.database import Database, DatabaseArgs
from components.edge import Edge, EdgeArgs

config = pulumi.Config()
landing_zone_stack_name = config.require("landingZoneStack")
db_engine_version = config.get("dbEngineVersion") or "16"
function_memory_mb = config.get_int("functionMemoryMB") or 2048
website_dist_path = config.get("websiteDistPath") or "./website/dist"
api_handler_path = config.get("apiHandlerPath") or "./api/dist"
location = pulumi.Config("azure-native").require("location")

landing_zone = pulumi.StackReference(landing_zone_stack_name)
resource_group_name = landing_zone.require_output("resourceGroupName")
vnet_id = landing_zone.require_output("networkId")
private_subnet_ids = landing_zone.require_output("privateSubnetIds")
key_vault_name = landing_zone.require_output("secretsStore")

project_name = f"{pulumi.get_stack()}-serverless-react-postgres"
common_tags = {
    "environment": pulumi.get_stack(),
    "solution-family": "serverless-react-postgres",
    "cloud": "azure",
    "language": "python",
}

database = Database(
    "db",
    DatabaseArgs(
        resource_group_name=resource_group_name,
        location=location,
        vnet_id=vnet_id,
        private_subnet_ids=private_subnet_ids,
        key_vault_name=key_vault_name,
        engine_version=db_engine_version,
        name_prefix=project_name,
        tags=common_tags,
    ),
)

edge = Edge(
    "edge",
    EdgeArgs(
        resource_group_name=resource_group_name,
        location=location,
        vnet_id=vnet_id,
        key_vault_name=key_vault_name,
        database_secret_uri=database.secret_uri,
        website_dist_path=website_dist_path,
        api_handler_path=api_handler_path,
        function_memory_mb=function_memory_mb,
        name_prefix=project_name,
        tags=common_tags,
    ),
)

pulumi.export("siteUrl", edge.site_url)
pulumi.export("apiUrl", edge.api_url)
pulumi.export("dbSecretId", database.secret_uri)
pulumi.export("dbServerName", database.server_name)
pulumi.export("functionAppName", edge.function_app_name)
pulumi.export("frontDoorEndpointHostname", edge.endpoint_hostname)
pulumi.export("escEnvironment", f"{pulumi.get_stack()}-serverless-react-postgres")
package main

import (
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"

	"serverless-react-postgres-azure/database"
	"serverless-react-postgres-azure/edge"
)

func main() {
	pulumi.Run(Program)
}

func Program(ctx *pulumi.Context) error {
	cfg := config.New(ctx, "")
	landingZoneStackName := cfg.Require("landingZoneStack")
	dbEngineVersion := cfg.Get("dbEngineVersion")
	if dbEngineVersion == "" {
		dbEngineVersion = "16"
	}
	functionMemoryMB := cfg.GetInt("functionMemoryMB")
	if functionMemoryMB == 0 {
		functionMemoryMB = 2048
	}
	websiteDistPath := cfg.Get("websiteDistPath")
	if websiteDistPath == "" {
		websiteDistPath = "./website/dist"
	}
	apiHandlerPath := cfg.Get("apiHandlerPath")
	if apiHandlerPath == "" {
		apiHandlerPath = "./api/dist"
	}
	location := config.New(ctx, "azure-native").Require("location")

	landingZone, err := pulumi.NewStackReference(ctx, landingZoneStackName, nil)
	if err != nil {
		return err
	}
	resourceGroupName := landingZone.GetStringOutput(pulumi.String("resourceGroupName"))
	vnetId := landingZone.GetStringOutput(pulumi.String("networkId"))
	privateSubnetIds := landingZone.GetOutput(pulumi.String("privateSubnetIds")).ApplyT(func(v interface{}) []string {
		values, ok := v.([]interface{})
		if !ok {
			return []string{}
		}
		result := make([]string, 0, len(values))
		for _, item := range values {
			if s, ok := item.(string); ok {
				result = append(result, s)
			}
		}
		return result
	}).(pulumi.StringArrayOutput)
	keyVaultName := landingZone.GetStringOutput(pulumi.String("secretsStore"))

	projectName := ctx.Stack() + "-serverless-react-postgres"
	commonTags := pulumi.StringMap{
		"environment":     pulumi.String(ctx.Stack()),
		"solution-family": pulumi.String("serverless-react-postgres"),
		"cloud":           pulumi.String("azure"),
		"language":        pulumi.String("go"),
	}

	db, err := database.New(ctx, "db", &database.Args{
		ResourceGroupName: resourceGroupName,
		Location:          pulumi.String(location),
		VnetId:            vnetId,
		PrivateSubnetIds:  privateSubnetIds,
		KeyVaultName:      keyVaultName,
		EngineVersion:     pulumi.String(dbEngineVersion),
		NamePrefix:        pulumi.String(projectName),
		Tags:              commonTags,
	})
	if err != nil {
		return err
	}

	ed, err := edge.New(ctx, "edge", &edge.Args{
		ResourceGroupName: resourceGroupName,
		Location:          pulumi.String(location),
		VnetId:            vnetId,
		KeyVaultName:      keyVaultName,
		DatabaseSecretUri: db.SecretUri,
		WebsiteDistPath:   websiteDistPath,
		ApiHandlerPath:    apiHandlerPath,
		FunctionMemoryMB:  pulumi.Int(functionMemoryMB),
		NamePrefix:        pulumi.String(projectName),
		Tags:              commonTags,
	})
	if err != nil {
		return err
	}

	ctx.Export("siteUrl", ed.SiteUrl)
	ctx.Export("apiUrl", ed.ApiUrl)
	ctx.Export("dbSecretId", db.SecretUri)
	ctx.Export("dbServerName", db.ServerName)
	ctx.Export("functionAppName", ed.FunctionAppName)
	ctx.Export("frontDoorEndpointHostname", ed.EndpointHostname)
	ctx.Export("escEnvironment", pulumi.String(ctx.Stack()+"-serverless-react-postgres"))
	return nil
}

Reusable components

The database wiring and the CDN + function + bucket wiring each live in a reusable module. Copy them into other Pulumi projects or adapt per team.

components/database.ts

Provisions the PostgreSQL Flexible Server instance on the landing-zone private network, generates a strong database password, and stores it in Azure Key Vault for the function to read.

import * as azure from "@pulumi/azure-native";
import * as pulumi from "@pulumi/pulumi";
import * as random from "@pulumi/random";

export interface DatabaseArgs {
    resourceGroupName: pulumi.Input<string>;
    location: pulumi.Input<string>;
    vnetId: pulumi.Input<string>;
    privateSubnetIds: pulumi.Input<pulumi.Input<string>[]>;
    keyVaultName: pulumi.Input<string>;
    engineVersion: pulumi.Input<string>;
    namePrefix: pulumi.Input<string>;
    tags: Record<string, string>;
}

const PRIVATE_DNS_ZONE_NAME = "privatepostgres.database.azure.com";

/**
 * Azure Database for PostgreSQL Flexible Server wired for private VNet access.
 *
 * Assumptions:
 *   - The landing-zone VNet (`args.vnetId`) exposes at least one private subnet that is
 *     delegated to `Microsoft.DBforPostgreSQL/flexibleServers`. We consume
 *     `privateSubnetIds[0]` as the DB subnet. If the landing-zone does not delegate that
 *     subnet, this deploy will fail fast and you should add the delegation there rather
 *     than here, so the VNet schema remains owned by the landing-zone stack.
 *   - The landing-zone Key Vault (`args.keyVaultName`) has RBAC enabled. The full DATABASE_URL
 *     is written as a vault secret; the Function App references it via
 *     `@Microsoft.KeyVault(SecretUri=...)`.
 */
export class Database extends pulumi.ComponentResource {
    public readonly serverName: pulumi.Output<string>;
    public readonly secretUri: pulumi.Output<string>;
    public readonly fullyQualifiedDomainName: pulumi.Output<string>;
    public readonly databaseName: pulumi.Output<string>;

    constructor(name: string, args: DatabaseArgs, opts?: pulumi.ComponentResourceOptions) {
        super("serverless-react-postgres:azure:Database", name, {}, opts);
        const parent = { parent: this };

        const databaseName = "appdb";
        const masterUsername = "pgadmin";

        const password = new random.RandomPassword(`${name}-password`, {
            length: 32,
            special: false,
        }, parent);

        // Private DNS zone + VNet link so the Function App's VNet integration can
        // resolve `<server>.privatepostgres.database.azure.com` inside the landing-zone VNet.
        const privateDnsZone = new azure.privatedns.PrivateZone(`${name}-dns`, {
            resourceGroupName: args.resourceGroupName,
            privateZoneName: PRIVATE_DNS_ZONE_NAME,
            location: "global",
            tags: args.tags,
        }, parent);

        const dnsVnetLink = new azure.privatedns.VirtualNetworkLink(`${name}-dns-link`, {
            resourceGroupName: args.resourceGroupName,
            privateZoneName: privateDnsZone.name,
            location: "global",
            registrationEnabled: false,
            virtualNetwork: { id: args.vnetId },
            tags: args.tags,
        }, parent);

        const dbSubnetId = pulumi.output(args.privateSubnetIds).apply((ids) => {
            if (!ids || ids.length === 0) {
                throw new Error("privateSubnetIds must have at least one subnet for the Flexible Server");
            }
            return ids[0];
        });

        const serverNameOutput = pulumi
            .all([args.namePrefix])
            .apply(([prefix]) => prefix.toLowerCase().replace(/[^a-z0-9-]/g, "-").slice(0, 55));

        const server = new azure.dbforpostgresql.Server(`${name}-pg`, {
            resourceGroupName: args.resourceGroupName,
            location: args.location,
            serverName: serverNameOutput,
            version: args.engineVersion,
            administratorLogin: masterUsername,
            administratorLoginPassword: password.result,
            sku: {
                name: "Standard_B1ms",
                tier: azure.dbforpostgresql.SkuTier.Burstable,
            },
            storage: {
                storageSizeGB: 32,
            },
            backup: {
                backupRetentionDays: 7,
                geoRedundantBackup: azure.dbforpostgresql.GeoRedundantBackup.Disabled,
            },
            highAvailability: {
                mode: azure.dbforpostgresql.PostgreSqlFlexibleServerHighAvailabilityMode.Disabled,
            },
            network: {
                delegatedSubnetResourceId: dbSubnetId,
                privateDnsZoneArmResourceId: privateDnsZone.id,
            },
            createMode: azure.dbforpostgresql.CreateMode.Default,
            tags: args.tags,
        }, { ...parent, dependsOn: [dnsVnetLink] });

        const appDatabase = new azure.dbforpostgresql.Database(`${name}-db`, {
            resourceGroupName: args.resourceGroupName,
            serverName: server.name,
            databaseName: databaseName,
            charset: "UTF8",
            collation: "en_US.utf8",
        }, parent);

        const connectionUrl = pulumi.all([
            password.result,
            server.fullyQualifiedDomainName,
        ]).apply(([pw, host]) =>
            `postgresql://${masterUsername}:${encodeURIComponent(pw)}@${host}:5432/${databaseName}?sslmode=require`,
        );

        const secretName = pulumi.interpolate`${args.namePrefix}-database-url`.apply((n) =>
            n.replace(/[^A-Za-z0-9-]/g, "-"),
        );

        const secret = new azure.keyvault.Secret(`${name}-secret`, {
            resourceGroupName: args.resourceGroupName,
            vaultName: args.keyVaultName,
            secretName: secretName,
            properties: {
                value: connectionUrl,
                contentType: "text/plain",
            },
            tags: args.tags,
        }, { ...parent, dependsOn: [appDatabase] });

        // Key Vault reference syntax requires the secret URI; the resource exposes
        // `properties.secretUri` (no version) which is what `@Microsoft.KeyVault(SecretUri=...)`
        // expects when we want app settings to always follow the latest version.
        this.serverName = server.name;
        this.secretUri = secret.properties.apply((p) => p.secretUri);
        this.fullyQualifiedDomainName = server.fullyQualifiedDomainName;
        this.databaseName = pulumi.output(databaseName);

        this.registerOutputs({
            serverName: this.serverName,
            secretUri: this.secretUri,
            fullyQualifiedDomainName: this.fullyQualifiedDomainName,
            databaseName: this.databaseName,
        });
    }
}

components/edge.ts

Provisions the Azure Functions (Flex Consumption) function that runs the API, uploads the SPA to object storage, and wires Azure Front Door Standard so /* serves the SPA and /api/* reaches the function.

import * as fs from "fs";
import * as path from "path";

import * as azure from "@pulumi/azure-native";
import * as pulumi from "@pulumi/pulumi";

export interface EdgeArgs {
    resourceGroupName: pulumi.Input<string>;
    location: pulumi.Input<string>;
    vnetId: pulumi.Input<string>;
    keyVaultName: pulumi.Input<string>;
    databaseSecretUri: pulumi.Input<string>;
    websiteDistPath: string;
    apiHandlerPath: string;
    functionMemoryMB: pulumi.Input<number>;
    namePrefix: pulumi.Input<string>;
    tags: Record<string, string>;
}

const MIME_TYPES: Record<string, string> = {
    ".html": "text/html; charset=utf-8",
    ".js": "application/javascript; charset=utf-8",
    ".mjs": "application/javascript; charset=utf-8",
    ".css": "text/css; charset=utf-8",
    ".json": "application/json; charset=utf-8",
    ".map": "application/json; charset=utf-8",
    ".svg": "image/svg+xml",
    ".png": "image/png",
    ".jpg": "image/jpeg",
    ".jpeg": "image/jpeg",
    ".gif": "image/gif",
    ".ico": "image/x-icon",
    ".webp": "image/webp",
    ".txt": "text/plain; charset=utf-8",
    ".woff": "font/woff",
    ".woff2": "font/woff2",
};

function contentTypeFor(filePath: string): string {
    const ext = path.extname(filePath).toLowerCase();
    return MIME_TYPES[ext] ?? "application/octet-stream";
}

function walk(dir: string): string[] {
    const results: string[] = [];
    for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
        const full = path.join(dir, entry.name);
        if (entry.isDirectory()) {
            results.push(...walk(full));
        } else if (entry.isFile()) {
            results.push(full);
        }
    }
    return results;
}

function buildArchive(root: string): pulumi.asset.AssetArchive {
    const map: Record<string, pulumi.asset.FileAsset> = {};
    for (const full of walk(root)) {
        const rel = path.relative(root, full).split(path.sep).join("/");
        map[rel] = new pulumi.asset.FileAsset(full);
    }
    return new pulumi.asset.AssetArchive(map);
}

export class Edge extends pulumi.ComponentResource {
    public readonly siteUrl: pulumi.Output<string>;
    public readonly apiUrl: pulumi.Output<string>;
    public readonly functionAppName: pulumi.Output<string>;
    public readonly endpointHostname: pulumi.Output<string>;
    public readonly storageAccountName: pulumi.Output<string>;

    constructor(name: string, args: EdgeArgs, opts?: pulumi.ComponentResourceOptions) {
        super("serverless-react-postgres:azure:Edge", name, {}, opts);
        const parent = { parent: this };

        const client = azure.authorization.getClientConfigOutput({ parent: this });

        // Dedicated storage account for this blueprint (SPA + function zip).
        const storageAccountNameInput = pulumi
            .output(args.namePrefix)
            .apply((prefix) => prefix.toLowerCase().replace(/[^a-z0-9]/g, "").slice(0, 20));

        const storageAccount = new azure.storage.StorageAccount(`${name}-storage`, {
            accountName: storageAccountNameInput,
            resourceGroupName: args.resourceGroupName,
            location: args.location,
            sku: { name: azure.storage.SkuName.Standard_LRS },
            kind: azure.storage.Kind.StorageV2,
            allowBlobPublicAccess: true,
            minimumTlsVersion: azure.storage.MinimumTlsVersion.TLS1_2,
            tags: args.tags,
        }, parent);

        // Enable the $web static-website endpoint; this provisions the $web container
        // automatically and exposes `primaryEndpoints.web`.
        const staticWebsite = new azure.storage.StorageAccountStaticWebsite(`${name}-website`, {
            accountName: storageAccount.name,
            resourceGroupName: args.resourceGroupName,
            indexDocument: "index.html",
            error404Document: "index.html",
        }, parent);

        const codeContainer = new azure.storage.BlobContainer(`${name}-code`, {
            accountName: storageAccount.name,
            containerName: "function-code",
            resourceGroupName: args.resourceGroupName,
            publicAccess: azure.storage.PublicAccess.None,
        }, parent);

        // Per-file SPA upload into $web so each file keeps its correct Content-Type.
        const websiteFiles = walk(args.websiteDistPath);
        for (const file of websiteFiles) {
            const key = path.relative(args.websiteDistPath, file).split(path.sep).join("/");
            const safeKey = key.replace(/[^A-Za-z0-9._-]/g, "_");
            new azure.storage.Blob(`${name}-site-${safeKey}`, {
                accountName: storageAccount.name,
                resourceGroupName: args.resourceGroupName,
                containerName: staticWebsite.containerName,
                blobName: key,
                source: new pulumi.asset.FileAsset(file),
                contentType: contentTypeFor(file),
                type: azure.storage.BlobType.Block,
            }, { ...parent, dependsOn: [staticWebsite] });
        }

        // Function App zip package: bundle `./api/dist` into a single zip blob that
        // Flex Consumption pulls from via `functionAppConfig.deployment`.
        const functionZip = new azure.storage.Blob(`${name}-fn-zip`, {
            accountName: storageAccount.name,
            resourceGroupName: args.resourceGroupName,
            containerName: codeContainer.name,
            blobName: "functionapp.zip",
            source: buildArchive(args.apiHandlerPath),
            contentType: "application/zip",
            type: azure.storage.BlobType.Block,
        }, parent);

        const storageKeys = azure.storage.listStorageAccountKeysOutput({
            accountName: storageAccount.name,
            resourceGroupName: args.resourceGroupName,
        }, { parent: this });
        const storageConnectionString = pulumi
            .all([storageAccount.name, storageKeys.keys])
            .apply(([accountName, keys]) =>
                `DefaultEndpointsProtocol=https;AccountName=${accountName};AccountKey=${keys[0].value};EndpointSuffix=core.windows.net`,
            );

        // Dedicated subnet for the Function App VNet integration. The Web App platform
        // requires a subnet delegated to `Microsoft.Web/serverFarms`, and the landing-zone
        // subnets are general-purpose, so we carve out a /26 here. Address prefix is
        // conventional for the 10.10.0.0/16 landing-zone VNet; adjust via config if a
        // collision is reported at deploy time.
        const vnetName = pulumi.output(args.vnetId).apply((id) => {
            const parts = id.split("/");
            return parts[parts.length - 1];
        });
        const webSubnet = new azure.network.Subnet(`${name}-web-subnet`, {
            subnetName: pulumi.interpolate`${args.namePrefix}-web-sn`,
            resourceGroupName: args.resourceGroupName,
            virtualNetworkName: vnetName,
            addressPrefix: "10.10.240.0/26",
            delegations: [{
                name: "web",
                serviceName: "Microsoft.Web/serverFarms",
            }],
        }, parent);

        const plan = new azure.web.AppServicePlan(`${name}-plan`, {
            kind: "functionapp",
            location: args.location,
            resourceGroupName: args.resourceGroupName,
            reserved: true,
            sku: { name: "FC1", tier: "FlexConsumption" },
            tags: args.tags,
        }, parent);

        const keyVaultReference = pulumi
            .output(args.databaseSecretUri)
            .apply((uri) => `@Microsoft.KeyVault(SecretUri=${uri})`);

        const functionApp = new azure.web.WebApp(`${name}-fn`, {
            kind: "functionapp,linux",
            location: args.location,
            resourceGroupName: args.resourceGroupName,
            serverFarmId: plan.id,
            httpsOnly: true,
            identity: {
                type: azure.web.ManagedServiceIdentityType.SystemAssigned,
            },
            virtualNetworkSubnetId: webSubnet.id,
            functionAppConfig: {
                deployment: {
                    storage: {
                        type: azure.web.FunctionsDeploymentStorageType.BlobContainer,
                        value: pulumi.interpolate`${storageAccount.primaryEndpoints.blob}${codeContainer.name}/${functionZip.name}`,
                        authentication: {
                            type: azure.web.AuthenticationType.StorageAccountConnectionString,
                            storageAccountConnectionStringName: "AzureWebJobsStorage",
                        },
                    },
                },
                runtime: {
                    name: azure.web.RuntimeName.Node,
                    version: "20",
                },
                scaleAndConcurrency: {
                    instanceMemoryMB: args.functionMemoryMB,
                    maximumInstanceCount: 100,
                },
            },
            siteConfig: {
                linuxFxVersion: "Node|20",
                appSettings: [
                    { name: "AzureWebJobsStorage", value: storageConnectionString },
                    { name: "FUNCTIONS_EXTENSION_VERSION", value: "~4" },
                    { name: "DATABASE_URL", value: keyVaultReference },
                ],
            },
            tags: args.tags,
        }, { ...parent, dependsOn: [functionZip] });

        // Grant the Function App's system-assigned identity Key Vault Secrets User so
        // the Key Vault reference in `DATABASE_URL` resolves at runtime.
        const keyVaultSecretsUser = "4633458b-17de-408a-b874-0445c86b69e6";
        const keyVaultScope = pulumi.interpolate`/subscriptions/${client.subscriptionId}/resourceGroups/${args.resourceGroupName}/providers/Microsoft.KeyVault/vaults/${args.keyVaultName}`;
        new azure.authorization.RoleAssignment(`${name}-fn-kv-access`, {
            principalId: functionApp.identity.apply((id) => id!.principalId!),
            principalType: "ServicePrincipal",
            roleDefinitionId: pulumi.interpolate`/subscriptions/${client.subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/${keyVaultSecretsUser}`,
            scope: keyVaultScope,
        }, parent);

        // Front Door Standard: single endpoint, two origin groups, two routes.
        const edgeProfile = new azure.cdn.Profile(`${name}-edge`, {
            resourceGroupName: args.resourceGroupName,
            location: "global",
            sku: { name: azure.cdn.SkuName.Standard_AzureFrontDoor },
            tags: args.tags,
        }, parent);

        const edgeEndpoint = new azure.cdn.AFDEndpoint(`${name}-endpoint`, {
            profileName: edgeProfile.name,
            resourceGroupName: args.resourceGroupName,
            location: "global",
            enabledState: azure.cdn.EnabledState.Enabled,
            tags: args.tags,
        }, parent);

        const webOriginHost = storageAccount.primaryEndpoints.web.apply((endpoint) =>
            endpoint.replace(/^https?:\/\//, "").replace(/\/$/, ""),
        );

        const websiteOriginGroup = new azure.cdn.AFDOriginGroup(`${name}-website-og`, {
            originGroupName: "website-origins",
            profileName: edgeProfile.name,
            resourceGroupName: args.resourceGroupName,
            loadBalancingSettings: {
                sampleSize: 4,
                successfulSamplesRequired: 3,
                additionalLatencyInMilliseconds: 50,
            },
            healthProbeSettings: {
                probePath: "/",
                probeRequestType: azure.cdn.HealthProbeRequestType.HEAD,
                probeProtocol: azure.cdn.ProbeProtocol.Https,
                probeIntervalInSeconds: 120,
            },
        }, parent);

        const websiteOrigin = new azure.cdn.AFDOrigin(`${name}-website-origin`, {
            originName: "website",
            originGroupName: websiteOriginGroup.name,
            profileName: edgeProfile.name,
            resourceGroupName: args.resourceGroupName,
            hostName: webOriginHost,
            originHostHeader: webOriginHost,
            httpsPort: 443,
            enabledState: azure.cdn.EnabledState.Enabled,
        }, { ...parent, dependsOn: [staticWebsite] });

        const apiOriginGroup = new azure.cdn.AFDOriginGroup(`${name}-api-og`, {
            originGroupName: "api-origins",
            profileName: edgeProfile.name,
            resourceGroupName: args.resourceGroupName,
            loadBalancingSettings: {
                sampleSize: 4,
                successfulSamplesRequired: 3,
                additionalLatencyInMilliseconds: 50,
            },
            healthProbeSettings: {
                probePath: "/api/health",
                probeRequestType: azure.cdn.HealthProbeRequestType.GET,
                probeProtocol: azure.cdn.ProbeProtocol.Https,
                probeIntervalInSeconds: 120,
            },
        }, parent);

        const apiOrigin = new azure.cdn.AFDOrigin(`${name}-api-origin`, {
            originName: "api",
            originGroupName: apiOriginGroup.name,
            profileName: edgeProfile.name,
            resourceGroupName: args.resourceGroupName,
            hostName: functionApp.defaultHostName,
            originHostHeader: functionApp.defaultHostName,
            httpsPort: 443,
            enabledState: azure.cdn.EnabledState.Enabled,
        }, parent);

        new azure.cdn.Route(`${name}-website-route`, {
            routeName: "website",
            endpointName: edgeEndpoint.name,
            profileName: edgeProfile.name,
            resourceGroupName: args.resourceGroupName,
            originGroup: { id: websiteOriginGroup.id },
            supportedProtocols: [
                azure.cdn.AFDEndpointProtocols.Http,
                azure.cdn.AFDEndpointProtocols.Https,
            ],
            patternsToMatch: ["/*"],
            forwardingProtocol: azure.cdn.ForwardingProtocol.HttpsOnly,
            httpsRedirect: azure.cdn.HttpsRedirect.Enabled,
            linkToDefaultDomain: azure.cdn.LinkToDefaultDomain.Enabled,
            enabledState: azure.cdn.EnabledState.Enabled,
        }, { ...parent, dependsOn: [websiteOrigin] });

        new azure.cdn.Route(`${name}-api-route`, {
            routeName: "api",
            endpointName: edgeEndpoint.name,
            profileName: edgeProfile.name,
            resourceGroupName: args.resourceGroupName,
            originGroup: { id: apiOriginGroup.id },
            supportedProtocols: [
                azure.cdn.AFDEndpointProtocols.Http,
                azure.cdn.AFDEndpointProtocols.Https,
            ],
            patternsToMatch: ["/api/*"],
            forwardingProtocol: azure.cdn.ForwardingProtocol.HttpsOnly,
            httpsRedirect: azure.cdn.HttpsRedirect.Enabled,
            linkToDefaultDomain: azure.cdn.LinkToDefaultDomain.Enabled,
            enabledState: azure.cdn.EnabledState.Enabled,
        }, { ...parent, dependsOn: [apiOrigin] });

        this.siteUrl = edgeEndpoint.hostName.apply((host) => `https://${host}`);
        this.apiUrl = edgeEndpoint.hostName.apply((host) => `https://${host}/api`);
        this.functionAppName = functionApp.name;
        this.endpointHostname = edgeEndpoint.hostName;
        this.storageAccountName = storageAccount.name;

        this.registerOutputs({
            siteUrl: this.siteUrl,
            apiUrl: this.apiUrl,
            functionAppName: this.functionAppName,
            endpointHostname: this.endpointHostname,
            storageAccountName: this.storageAccountName,
        });
    }
}

components/database.py

Provisions the PostgreSQL Flexible Server instance on the landing-zone private network, generates a strong database password, and stores it in Azure Key Vault for the function to read.

from __future__ import annotations

from dataclasses import dataclass
from typing import Mapping, Optional, Sequence
from urllib.parse import quote

import pulumi
import pulumi_azure_native as azure_native
import pulumi_random as random


PRIVATE_DNS_ZONE_NAME = "privatepostgres.database.azure.com"


@dataclass
class DatabaseArgs:
    resource_group_name: pulumi.Input[str]
    location: pulumi.Input[str]
    vnet_id: pulumi.Input[str]
    private_subnet_ids: pulumi.Input[Sequence[pulumi.Input[str]]]
    key_vault_name: pulumi.Input[str]
    engine_version: pulumi.Input[str]
    name_prefix: pulumi.Input[str]
    tags: Optional[Mapping[str, str]] = None


class Database(pulumi.ComponentResource):
    """Azure Database for PostgreSQL Flexible Server wired for private VNet access.

    Assumptions:
      - The landing-zone VNet exposes at least one private subnet delegated to
        `Microsoft.DBforPostgreSQL/flexibleServers`. We use `private_subnet_ids[0]` as
        the DB subnet. If the landing-zone does not delegate that subnet, deploy will
        fail fast and the delegation should be added there so VNet ownership stays
        with the landing-zone stack.
      - The landing-zone Key Vault is RBAC-enabled. The DATABASE_URL is stored as a
        vault secret; the Function App references it via
        `@Microsoft.KeyVault(SecretUri=...)`.
    """

    def __init__(
        self,
        name: str,
        args: DatabaseArgs,
        opts: Optional[pulumi.ResourceOptions] = None,
    ) -> None:
        super().__init__("serverless-react-postgres:azure:Database", name, {}, opts)
        tags = dict(args.tags or {})
        child = pulumi.ResourceOptions(parent=self)

        database_name = "appdb"
        master_username = "pgadmin"

        password = random.RandomPassword(
            f"{name}-password",
            length=32,
            special=False,
            opts=child,
        )

        private_dns_zone = azure_native.privatedns.PrivateZone(
            f"{name}-dns",
            resource_group_name=args.resource_group_name,
            private_zone_name=PRIVATE_DNS_ZONE_NAME,
            location="global",
            tags=tags,
            opts=child,
        )

        dns_vnet_link = azure_native.privatedns.VirtualNetworkLink(
            f"{name}-dns-link",
            resource_group_name=args.resource_group_name,
            private_zone_name=private_dns_zone.name,
            location="global",
            registration_enabled=False,
            virtual_network=azure_native.privatedns.SubResourceArgs(id=args.vnet_id),
            tags=tags,
            opts=child,
        )

        def _first_subnet(ids):
            if not ids:
                raise ValueError("private_subnet_ids must have at least one subnet for the Flexible Server")
            return ids[0]

        db_subnet_id = pulumi.Output.from_input(args.private_subnet_ids).apply(_first_subnet)

        server_name_output = pulumi.Output.from_input(args.name_prefix).apply(
            lambda prefix: "".join(ch if ch.isalnum() or ch == "-" else "-" for ch in prefix.lower())[:55]
        )

        server = azure_native.dbforpostgresql.Server(
            f"{name}-pg",
            resource_group_name=args.resource_group_name,
            location=args.location,
            server_name=server_name_output,
            version=args.engine_version,
            administrator_login=master_username,
            administrator_login_password=password.result,
            sku=azure_native.dbforpostgresql.SkuArgs(
                name="Standard_B1ms",
                tier=azure_native.dbforpostgresql.SkuTier.BURSTABLE,
            ),
            storage=azure_native.dbforpostgresql.StorageArgs(storage_size_gb=32),
            backup=azure_native.dbforpostgresql.BackupArgs(
                backup_retention_days=7,
                geo_redundant_backup=azure_native.dbforpostgresql.GeoRedundantBackup.DISABLED,
            ),
            high_availability=azure_native.dbforpostgresql.HighAvailabilityArgs(
                mode=azure_native.dbforpostgresql.PostgreSqlFlexibleServerHighAvailabilityMode.DISABLED,
            ),
            network=azure_native.dbforpostgresql.NetworkArgs(
                delegated_subnet_resource_id=db_subnet_id,
                private_dns_zone_arm_resource_id=private_dns_zone.id,
            ),
            create_mode=azure_native.dbforpostgresql.CreateMode.DEFAULT,
            tags=tags,
            opts=pulumi.ResourceOptions(parent=self, depends_on=[dns_vnet_link]),
        )

        app_database = azure_native.dbforpostgresql.Database(
            f"{name}-db",
            resource_group_name=args.resource_group_name,
            server_name=server.name,
            database_name=database_name,
            charset="UTF8",
            collation="en_US.utf8",
            opts=child,
        )

        connection_url = pulumi.Output.all(password.result, server.fully_qualified_domain_name).apply(
            lambda parts: f"postgresql://{master_username}:{quote(parts[0], safe='')}@{parts[1]}:5432/{database_name}?sslmode=require"
        )

        secret_name = pulumi.Output.from_input(args.name_prefix).apply(
            lambda prefix: "".join(ch if ch.isalnum() or ch == "-" else "-" for ch in prefix) + "-database-url"
        )

        secret = azure_native.keyvault.Secret(
            f"{name}-secret",
            resource_group_name=args.resource_group_name,
            vault_name=args.key_vault_name,
            secret_name=secret_name,
            properties=azure_native.keyvault.SecretPropertiesArgs(
                value=connection_url,
                content_type="text/plain",
            ),
            tags=tags,
            opts=pulumi.ResourceOptions(parent=self, depends_on=[app_database]),
        )

        # `properties.secret_uri` (version-less) is what Key Vault references of the
        # form `@Microsoft.KeyVault(SecretUri=...)` expect when app settings should
        # always follow the latest secret version.
        self.server_name = server.name
        self.secret_uri = secret.properties.apply(lambda p: p.secret_uri)
        self.fully_qualified_domain_name = server.fully_qualified_domain_name
        self.database_name = pulumi.Output.from_input(database_name)

        self.register_outputs(
            {
                "server_name": self.server_name,
                "secret_uri": self.secret_uri,
                "fully_qualified_domain_name": self.fully_qualified_domain_name,
                "database_name": self.database_name,
            }
        )

components/edge.py

Provisions the Azure Functions (Flex Consumption) function that runs the API, uploads the SPA to object storage, and wires Azure Front Door Standard so /* serves the SPA and /api/* reaches the function.

from __future__ import annotations

import os
from dataclasses import dataclass
from typing import Mapping, Optional

import pulumi
import pulumi_azure_native as azure_native


MIME_TYPES = {
    ".html": "text/html; charset=utf-8",
    ".js": "application/javascript; charset=utf-8",
    ".mjs": "application/javascript; charset=utf-8",
    ".css": "text/css; charset=utf-8",
    ".json": "application/json; charset=utf-8",
    ".map": "application/json; charset=utf-8",
    ".svg": "image/svg+xml",
    ".png": "image/png",
    ".jpg": "image/jpeg",
    ".jpeg": "image/jpeg",
    ".gif": "image/gif",
    ".ico": "image/x-icon",
    ".webp": "image/webp",
    ".txt": "text/plain; charset=utf-8",
    ".woff": "font/woff",
    ".woff2": "font/woff2",
}


def _content_type_for(path: str) -> str:
    _, ext = os.path.splitext(path.lower())
    return MIME_TYPES.get(ext, "application/octet-stream")


def _walk(root: str):
    for dirpath, _, filenames in os.walk(root):
        for filename in filenames:
            yield os.path.join(dirpath, filename)


def _build_archive(root: str) -> pulumi.AssetArchive:
    assets = {}
    for full in _walk(root):
        rel = os.path.relpath(full, root).replace(os.sep, "/")
        assets[rel] = pulumi.FileAsset(full)
    return pulumi.AssetArchive(assets)


@dataclass
class EdgeArgs:
    resource_group_name: pulumi.Input[str]
    location: pulumi.Input[str]
    vnet_id: pulumi.Input[str]
    key_vault_name: pulumi.Input[str]
    database_secret_uri: pulumi.Input[str]
    website_dist_path: str
    api_handler_path: str
    function_memory_mb: pulumi.Input[int]
    name_prefix: pulumi.Input[str]
    tags: Optional[Mapping[str, str]] = None


class Edge(pulumi.ComponentResource):
    def __init__(
        self,
        name: str,
        args: EdgeArgs,
        opts: Optional[pulumi.ResourceOptions] = None,
    ) -> None:
        super().__init__("serverless-react-postgres:azure:Edge", name, {}, opts)
        tags = dict(args.tags or {})
        child = pulumi.ResourceOptions(parent=self)

        client = azure_native.authorization.get_client_config_output()

        storage_account_name = pulumi.Output.from_input(args.name_prefix).apply(
            lambda prefix: "".join(ch for ch in prefix.lower() if ch.isalnum())[:20]
        )

        storage_account = azure_native.storage.StorageAccount(
            f"{name}-storage",
            account_name=storage_account_name,
            resource_group_name=args.resource_group_name,
            location=args.location,
            sku=azure_native.storage.SkuArgs(name=azure_native.storage.SkuName.STANDARD_LRS),
            kind=azure_native.storage.Kind.STORAGE_V2,
            allow_blob_public_access=True,
            minimum_tls_version=azure_native.storage.MinimumTlsVersion.TLS1_2,
            tags=tags,
            opts=child,
        )

        static_website = azure_native.storage.StorageAccountStaticWebsite(
            f"{name}-website",
            account_name=storage_account.name,
            resource_group_name=args.resource_group_name,
            index_document="index.html",
            error404_document="index.html",
            opts=child,
        )

        code_container = azure_native.storage.BlobContainer(
            f"{name}-code",
            account_name=storage_account.name,
            container_name="function-code",
            resource_group_name=args.resource_group_name,
            public_access=azure_native.storage.PublicAccess.NONE,
            opts=child,
        )

        # Per-file SPA upload into $web so each blob keeps its correct Content-Type.
        for full in _walk(args.website_dist_path):
            key = os.path.relpath(full, args.website_dist_path).replace(os.sep, "/")
            safe_key = "".join(ch if ch.isalnum() or ch in "._-" else "_" for ch in key)
            azure_native.storage.Blob(
                f"{name}-site-{safe_key}",
                account_name=storage_account.name,
                resource_group_name=args.resource_group_name,
                container_name=static_website.container_name,
                blob_name=key,
                source=pulumi.FileAsset(full),
                content_type=_content_type_for(full),
                type=azure_native.storage.BlobType.BLOCK,
                opts=pulumi.ResourceOptions(parent=self, depends_on=[static_website]),
            )

        function_zip = azure_native.storage.Blob(
            f"{name}-fn-zip",
            account_name=storage_account.name,
            resource_group_name=args.resource_group_name,
            container_name=code_container.name,
            blob_name="functionapp.zip",
            source=_build_archive(args.api_handler_path),
            content_type="application/zip",
            type=azure_native.storage.BlobType.BLOCK,
            opts=child,
        )

        storage_keys = azure_native.storage.list_storage_account_keys_output(
            account_name=storage_account.name,
            resource_group_name=args.resource_group_name,
        )
        storage_connection_string = pulumi.Output.all(
            storage_account.name, storage_keys.keys
        ).apply(
            lambda parts: f"DefaultEndpointsProtocol=https;AccountName={parts[0]};AccountKey={parts[1][0].value};EndpointSuffix=core.windows.net"
        )

        # Dedicated subnet for Function App VNet integration (delegated to Microsoft.Web/serverFarms).
        # Landing-zone private subnets are general-purpose; carve out a /26 in 10.10.240.0/26.
        vnet_name = pulumi.Output.from_input(args.vnet_id).apply(lambda vid: vid.split("/")[-1])
        web_subnet_name = pulumi.Output.from_input(args.name_prefix).apply(lambda p: f"{p}-web-sn")

        web_subnet = azure_native.network.Subnet(
            f"{name}-web-subnet",
            subnet_name=web_subnet_name,
            resource_group_name=args.resource_group_name,
            virtual_network_name=vnet_name,
            address_prefix="10.10.240.0/26",
            delegations=[
                azure_native.network.DelegationArgs(
                    name="web",
                    service_name="Microsoft.Web/serverFarms",
                ),
            ],
            opts=child,
        )

        plan = azure_native.web.AppServicePlan(
            f"{name}-plan",
            kind="functionapp",
            location=args.location,
            resource_group_name=args.resource_group_name,
            reserved=True,
            sku=azure_native.web.SkuDescriptionArgs(name="FC1", tier="FlexConsumption"),
            tags=tags,
            opts=child,
        )

        key_vault_reference = pulumi.Output.from_input(args.database_secret_uri).apply(
            lambda uri: f"@Microsoft.KeyVault(SecretUri={uri})"
        )

        deployment_value = pulumi.Output.all(
            storage_account.primary_endpoints.apply(lambda e: e.blob),
            code_container.name,
            function_zip.name,
        ).apply(lambda parts: f"{parts[0]}{parts[1]}/{parts[2]}")

        function_app = azure_native.web.WebApp(
            f"{name}-fn",
            kind="functionapp,linux",
            location=args.location,
            resource_group_name=args.resource_group_name,
            server_farm_id=plan.id,
            https_only=True,
            identity=azure_native.web.ManagedServiceIdentityArgs(
                type=azure_native.web.ManagedServiceIdentityType.SYSTEM_ASSIGNED,
            ),
            virtual_network_subnet_id=web_subnet.id,
            function_app_config=azure_native.web.FunctionAppConfigArgs(
                deployment=azure_native.web.FunctionsDeploymentArgs(
                    storage=azure_native.web.FunctionsDeploymentStorageArgs(
                        type=azure_native.web.FunctionsDeploymentStorageType.BLOB_CONTAINER,
                        value=deployment_value,
                        authentication=azure_native.web.FunctionsDeploymentAuthenticationArgs(
                            type=azure_native.web.AuthenticationType.STORAGE_ACCOUNT_CONNECTION_STRING,
                            storage_account_connection_string_name="AzureWebJobsStorage",
                        ),
                    ),
                ),
                runtime=azure_native.web.FunctionsRuntimeArgs(
                    name=azure_native.web.RuntimeName.NODE,
                    version="20",
                ),
                scale_and_concurrency=azure_native.web.FunctionsScaleAndConcurrencyArgs(
                    instance_memory_mb=args.function_memory_mb,
                    maximum_instance_count=100,
                ),
            ),
            site_config=azure_native.web.SiteConfigArgs(
                linux_fx_version="Node|20",
                app_settings=[
                    azure_native.web.NameValuePairArgs(name="AzureWebJobsStorage", value=storage_connection_string),
                    azure_native.web.NameValuePairArgs(name="FUNCTIONS_EXTENSION_VERSION", value="~4"),
                    azure_native.web.NameValuePairArgs(name="DATABASE_URL", value=key_vault_reference),
                ],
            ),
            tags=tags,
            opts=pulumi.ResourceOptions(parent=self, depends_on=[function_zip]),
        )

        key_vault_secrets_user = "4633458b-17de-408a-b874-0445c86b69e6"
        key_vault_scope = pulumi.Output.all(
            client.subscription_id, args.resource_group_name, args.key_vault_name
        ).apply(
            lambda parts: f"/subscriptions/{parts[0]}/resourceGroups/{parts[1]}/providers/Microsoft.KeyVault/vaults/{parts[2]}"
        )
        role_definition_id = client.subscription_id.apply(
            lambda sub: f"/subscriptions/{sub}/providers/Microsoft.Authorization/roleDefinitions/{key_vault_secrets_user}"
        )

        azure_native.authorization.RoleAssignment(
            f"{name}-fn-kv-access",
            principal_id=function_app.identity.apply(lambda i: i.principal_id if i else ""),
            principal_type="ServicePrincipal",
            role_definition_id=role_definition_id,
            scope=key_vault_scope,
            opts=child,
        )

        edge_profile = azure_native.cdn.Profile(
            f"{name}-edge",
            resource_group_name=args.resource_group_name,
            location="global",
            sku=azure_native.cdn.SkuArgs(name=azure_native.cdn.SkuName.STANDARD_AZURE_FRONT_DOOR),
            tags=tags,
            opts=child,
        )

        edge_endpoint = azure_native.cdn.AFDEndpoint(
            f"{name}-endpoint",
            profile_name=edge_profile.name,
            resource_group_name=args.resource_group_name,
            location="global",
            enabled_state=azure_native.cdn.EnabledState.ENABLED,
            tags=tags,
            opts=child,
        )

        web_origin_host = storage_account.primary_endpoints.apply(
            lambda e: e.web.replace("https://", "").replace("http://", "").rstrip("/")
        )

        website_origin_group = azure_native.cdn.AFDOriginGroup(
            f"{name}-website-og",
            origin_group_name="website-origins",
            profile_name=edge_profile.name,
            resource_group_name=args.resource_group_name,
            load_balancing_settings=azure_native.cdn.LoadBalancingSettingsParametersArgs(
                sample_size=4,
                successful_samples_required=3,
                additional_latency_in_milliseconds=50,
            ),
            health_probe_settings=azure_native.cdn.HealthProbeParametersArgs(
                probe_path="/",
                probe_request_type=azure_native.cdn.HealthProbeRequestType.HEAD,
                probe_protocol=azure_native.cdn.ProbeProtocol.HTTPS,
                probe_interval_in_seconds=120,
            ),
            opts=child,
        )

        website_origin = azure_native.cdn.AFDOrigin(
            f"{name}-website-origin",
            origin_name="website",
            origin_group_name=website_origin_group.name,
            profile_name=edge_profile.name,
            resource_group_name=args.resource_group_name,
            host_name=web_origin_host,
            origin_host_header=web_origin_host,
            https_port=443,
            enabled_state=azure_native.cdn.EnabledState.ENABLED,
            opts=pulumi.ResourceOptions(parent=self, depends_on=[static_website]),
        )

        api_origin_group = azure_native.cdn.AFDOriginGroup(
            f"{name}-api-og",
            origin_group_name="api-origins",
            profile_name=edge_profile.name,
            resource_group_name=args.resource_group_name,
            load_balancing_settings=azure_native.cdn.LoadBalancingSettingsParametersArgs(
                sample_size=4,
                successful_samples_required=3,
                additional_latency_in_milliseconds=50,
            ),
            health_probe_settings=azure_native.cdn.HealthProbeParametersArgs(
                probe_path="/api/health",
                probe_request_type=azure_native.cdn.HealthProbeRequestType.GET,
                probe_protocol=azure_native.cdn.ProbeProtocol.HTTPS,
                probe_interval_in_seconds=120,
            ),
            opts=child,
        )

        api_origin = azure_native.cdn.AFDOrigin(
            f"{name}-api-origin",
            origin_name="api",
            origin_group_name=api_origin_group.name,
            profile_name=edge_profile.name,
            resource_group_name=args.resource_group_name,
            host_name=function_app.default_host_name,
            origin_host_header=function_app.default_host_name,
            https_port=443,
            enabled_state=azure_native.cdn.EnabledState.ENABLED,
            opts=child,
        )

        azure_native.cdn.Route(
            f"{name}-website-route",
            route_name="website",
            endpoint_name=edge_endpoint.name,
            profile_name=edge_profile.name,
            resource_group_name=args.resource_group_name,
            origin_group=azure_native.cdn.ResourceReferenceArgs(id=website_origin_group.id),
            supported_protocols=[
                azure_native.cdn.AFDEndpointProtocols.HTTP,
                azure_native.cdn.AFDEndpointProtocols.HTTPS,
            ],
            patterns_to_match=["/*"],
            forwarding_protocol=azure_native.cdn.ForwardingProtocol.HTTPS_ONLY,
            https_redirect=azure_native.cdn.HttpsRedirect.ENABLED,
            link_to_default_domain=azure_native.cdn.LinkToDefaultDomain.ENABLED,
            enabled_state=azure_native.cdn.EnabledState.ENABLED,
            opts=pulumi.ResourceOptions(parent=self, depends_on=[website_origin]),
        )

        azure_native.cdn.Route(
            f"{name}-api-route",
            route_name="api",
            endpoint_name=edge_endpoint.name,
            profile_name=edge_profile.name,
            resource_group_name=args.resource_group_name,
            origin_group=azure_native.cdn.ResourceReferenceArgs(id=api_origin_group.id),
            supported_protocols=[
                azure_native.cdn.AFDEndpointProtocols.HTTP,
                azure_native.cdn.AFDEndpointProtocols.HTTPS,
            ],
            patterns_to_match=["/api/*"],
            forwarding_protocol=azure_native.cdn.ForwardingProtocol.HTTPS_ONLY,
            https_redirect=azure_native.cdn.HttpsRedirect.ENABLED,
            link_to_default_domain=azure_native.cdn.LinkToDefaultDomain.ENABLED,
            enabled_state=azure_native.cdn.EnabledState.ENABLED,
            opts=pulumi.ResourceOptions(parent=self, depends_on=[api_origin]),
        )

        self.site_url = edge_endpoint.host_name.apply(lambda host: f"https://{host}")
        self.api_url = edge_endpoint.host_name.apply(lambda host: f"https://{host}/api")
        self.function_app_name = function_app.name
        self.endpoint_hostname = edge_endpoint.host_name
        self.storage_account_name = storage_account.name

        self.register_outputs(
            {
                "site_url": self.site_url,
                "api_url": self.api_url,
                "function_app_name": self.function_app_name,
                "endpoint_hostname": self.endpoint_hostname,
                "storage_account_name": self.storage_account_name,
            }
        )

database/database.go

Provisions the PostgreSQL Flexible Server instance on the landing-zone private network, generates a strong database password, and stores it in Azure Key Vault for the function to read.

// Package database provisions an Azure Database for PostgreSQL Flexible Server
// wired for private VNet access, and stores the connection string in Key Vault.
//
// Assumptions:
//   - The landing-zone VNet exposes at least one private subnet delegated to
//     `Microsoft.DBforPostgreSQL/flexibleServers`. We use `PrivateSubnetIds[0]`.
//     If the subnet is not delegated, deploy will fail fast and the delegation
//     should be added in the landing-zone so VNet ownership stays with that stack.
//   - The landing-zone Key Vault is RBAC-enabled. The DATABASE_URL is stored as a
//     vault secret; the Function App references it via
//     `@Microsoft.KeyVault(SecretUri=...)`.
package database

import (
	"fmt"
	"net/url"
	"regexp"
	"strings"

	dbforpostgresql "github.com/pulumi/pulumi-azure-native-sdk/dbforpostgresql/v3"
	keyvault "github.com/pulumi/pulumi-azure-native-sdk/keyvault/v3"
	privatedns "github.com/pulumi/pulumi-azure-native-sdk/privatedns/v3"
	random "github.com/pulumi/pulumi-random/sdk/v4/go/random"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

const privateDnsZoneName = "privatepostgres.database.azure.com"

type Args struct {
	ResourceGroupName pulumi.StringInput
	Location          pulumi.StringInput
	VnetId            pulumi.StringInput
	PrivateSubnetIds  pulumi.StringArrayInput
	KeyVaultName      pulumi.StringInput
	EngineVersion     pulumi.StringInput
	NamePrefix        pulumi.StringInput
	Tags              pulumi.StringMapInput
}

type Database struct {
	pulumi.ResourceState

	ServerName               pulumi.StringOutput
	SecretUri                pulumi.StringOutput
	FullyQualifiedDomainName pulumi.StringOutput
	DatabaseName             pulumi.StringOutput
}

func New(ctx *pulumi.Context, name string, args *Args, opts ...pulumi.ResourceOption) (*Database, error) {
	d := &Database{}
	if err := ctx.RegisterComponentResource("serverless-react-postgres:azure:Database", name, d, opts...); err != nil {
		return nil, err
	}
	parent := pulumi.Parent(d)

	databaseName := "appdb"
	masterUsername := "pgadmin"

	password, err := random.NewRandomPassword(ctx, fmt.Sprintf("%s-password", name), &random.RandomPasswordArgs{
		Length:  pulumi.Int(32),
		Special: pulumi.Bool(false),
	}, parent)
	if err != nil {
		return nil, err
	}

	privateDnsZone, err := privatedns.NewPrivateZone(ctx, fmt.Sprintf("%s-dns", name), &privatedns.PrivateZoneArgs{
		ResourceGroupName: args.ResourceGroupName,
		PrivateZoneName:   pulumi.String(privateDnsZoneName),
		Location:          pulumi.String("global"),
		Tags:              args.Tags,
	}, parent)
	if err != nil {
		return nil, err
	}

	dnsVnetLink, err := privatedns.NewVirtualNetworkLink(ctx, fmt.Sprintf("%s-dns-link", name), &privatedns.VirtualNetworkLinkArgs{
		ResourceGroupName:   args.ResourceGroupName,
		PrivateZoneName:     privateDnsZone.Name,
		Location:            pulumi.String("global"),
		RegistrationEnabled: pulumi.Bool(false),
		VirtualNetwork: &privatedns.SubResourceArgs{
			Id: args.VnetId,
		},
		Tags: args.Tags,
	}, parent)
	if err != nil {
		return nil, err
	}

	dbSubnetId := args.PrivateSubnetIds.ToStringArrayOutput().ApplyT(func(ids []string) (string, error) {
		if len(ids) == 0 {
			return "", fmt.Errorf("PrivateSubnetIds must have at least one subnet for the Flexible Server")
		}
		return ids[0], nil
	}).(pulumi.StringOutput)

	nameRe := regexp.MustCompile("[^a-z0-9-]")
	serverNameOutput := args.NamePrefix.ToStringOutput().ApplyT(func(prefix string) string {
		cleaned := nameRe.ReplaceAllString(strings.ToLower(prefix), "-")
		if len(cleaned) > 55 {
			cleaned = cleaned[:55]
		}
		return cleaned
	}).(pulumi.StringOutput)

	server, err := dbforpostgresql.NewServer(ctx, fmt.Sprintf("%s-pg", name), &dbforpostgresql.ServerArgs{
		ResourceGroupName:          args.ResourceGroupName,
		Location:                   args.Location,
		ServerName:                 serverNameOutput,
		Version:                    args.EngineVersion,
		AdministratorLogin:         pulumi.String(masterUsername),
		AdministratorLoginPassword: password.Result,
		Sku: &dbforpostgresql.SkuArgs{
			Name: pulumi.String("Standard_B1ms"),
			Tier: pulumi.String(dbforpostgresql.SkuTierBurstable),
		},
		Storage: &dbforpostgresql.StorageArgs{
			StorageSizeGB: pulumi.Int(32),
		},
		Backup: &dbforpostgresql.BackupTypeArgs{
			BackupRetentionDays: pulumi.Int(7),
			GeoRedundantBackup:  pulumi.String(dbforpostgresql.GeoRedundantBackupDisabled),
		},
		HighAvailability: &dbforpostgresql.HighAvailabilityArgs{
			Mode: pulumi.String(dbforpostgresql.PostgreSqlFlexibleServerHighAvailabilityModeDisabled),
		},
		Network: &dbforpostgresql.NetworkArgs{
			DelegatedSubnetResourceId:   dbSubnetId,
			PrivateDnsZoneArmResourceId: privateDnsZone.ID().ToStringOutput(),
		},
		CreateMode: pulumi.String(dbforpostgresql.CreateModeDefault),
		Tags:       args.Tags,
	}, parent, pulumi.DependsOn([]pulumi.Resource{dnsVnetLink}))
	if err != nil {
		return nil, err
	}

	appDatabase, err := dbforpostgresql.NewDatabase(ctx, fmt.Sprintf("%s-db", name), &dbforpostgresql.DatabaseArgs{
		ResourceGroupName: args.ResourceGroupName,
		ServerName:        server.Name,
		DatabaseName:      pulumi.String(databaseName),
		Charset:           pulumi.String("UTF8"),
		Collation:         pulumi.String("en_US.utf8"),
	}, parent)
	if err != nil {
		return nil, err
	}

	connectionUrl := pulumi.All(password.Result, server.FullyQualifiedDomainName).ApplyT(func(parts []interface{}) string {
		pw := parts[0].(string)
		host := parts[1].(string)
		return fmt.Sprintf("postgresql://%s:%s@%s:5432/%s?sslmode=require",
			masterUsername, url.QueryEscape(pw), host, databaseName)
	}).(pulumi.StringOutput)

	secretNameRe := regexp.MustCompile("[^A-Za-z0-9-]")
	secretName := args.NamePrefix.ToStringOutput().ApplyT(func(prefix string) string {
		return secretNameRe.ReplaceAllString(prefix, "-") + "-database-url"
	}).(pulumi.StringOutput)

	secret, err := keyvault.NewSecret(ctx, fmt.Sprintf("%s-secret", name), &keyvault.SecretArgs{
		ResourceGroupName: args.ResourceGroupName,
		VaultName:         args.KeyVaultName,
		SecretName:        secretName,
		Properties: &keyvault.SecretPropertiesArgs{
			Value:       connectionUrl,
			ContentType: pulumi.String("text/plain"),
		},
		Tags: args.Tags,
	}, parent, pulumi.DependsOn([]pulumi.Resource{appDatabase}))
	if err != nil {
		return nil, err
	}

	d.ServerName = server.Name
	d.SecretUri = secret.Properties.ApplyT(func(p keyvault.SecretPropertiesResponse) string {
		return p.SecretUri
	}).(pulumi.StringOutput)
	d.FullyQualifiedDomainName = server.FullyQualifiedDomainName
	d.DatabaseName = pulumi.String(databaseName).ToStringOutput()

	if err := ctx.RegisterResourceOutputs(d, pulumi.Map{
		"serverName":               d.ServerName,
		"secretUri":                d.SecretUri,
		"fullyQualifiedDomainName": d.FullyQualifiedDomainName,
		"databaseName":             d.DatabaseName,
	}); err != nil {
		return nil, err
	}
	return d, nil
}

edge/edge.go

Provisions the Azure Functions (Flex Consumption) function that runs the API, uploads the SPA to object storage, and wires Azure Front Door Standard so /* serves the SPA and /api/* reaches the function.

// Package edge wires the Front Door + Storage static website + Flex Consumption
// Function App that serves the React SPA and Node API for this blueprint.
package edge

import (
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	authorization "github.com/pulumi/pulumi-azure-native-sdk/authorization/v3"
	cdn "github.com/pulumi/pulumi-azure-native-sdk/cdn/v3"
	network "github.com/pulumi/pulumi-azure-native-sdk/network/v3"
	storage "github.com/pulumi/pulumi-azure-native-sdk/storage/v3"
	web "github.com/pulumi/pulumi-azure-native-sdk/web/v3"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

var mimeTypes = map[string]string{
	".html":  "text/html; charset=utf-8",
	".js":    "application/javascript; charset=utf-8",
	".mjs":   "application/javascript; charset=utf-8",
	".css":   "text/css; charset=utf-8",
	".json":  "application/json; charset=utf-8",
	".map":   "application/json; charset=utf-8",
	".svg":   "image/svg+xml",
	".png":   "image/png",
	".jpg":   "image/jpeg",
	".jpeg": "image/jpeg",
	".gif":   "image/gif",
	".ico":   "image/x-icon",
	".webp":  "image/webp",
	".txt":   "text/plain; charset=utf-8",
	".woff":  "font/woff",
	".woff2": "font/woff2",
}

func contentTypeFor(path string) string {
	ext := strings.ToLower(filepath.Ext(path))
	if ct, ok := mimeTypes[ext]; ok {
		return ct
	}
	return "application/octet-stream"
}

func walkFiles(root string) ([]string, error) {
	var files []string
	if err := filepath.Walk(root, func(p string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if !info.IsDir() {
			files = append(files, p)
		}
		return nil
	}); err != nil {
		return nil, err
	}
	return files, nil
}

func buildArchive(root string) (pulumi.Archive, error) {
	files, err := walkFiles(root)
	if err != nil {
		return nil, err
	}
	assets := map[string]interface{}{}
	for _, full := range files {
		rel, err := filepath.Rel(root, full)
		if err != nil {
			return nil, err
		}
		key := filepath.ToSlash(rel)
		assets[key] = pulumi.NewFileAsset(full)
	}
	return pulumi.NewAssetArchive(assets), nil
}

var safeKeyRe = regexp.MustCompile("[^A-Za-z0-9._-]")

type Args struct {
	ResourceGroupName pulumi.StringInput
	Location          pulumi.StringInput
	VnetId            pulumi.StringInput
	KeyVaultName      pulumi.StringInput
	DatabaseSecretUri pulumi.StringInput
	WebsiteDistPath   string
	ApiHandlerPath    string
	FunctionMemoryMB  pulumi.IntInput
	NamePrefix        pulumi.StringInput
	Tags              pulumi.StringMapInput
}

type Edge struct {
	pulumi.ResourceState

	SiteUrl            pulumi.StringOutput
	ApiUrl             pulumi.StringOutput
	FunctionAppName    pulumi.StringOutput
	EndpointHostname   pulumi.StringOutput
	StorageAccountName pulumi.StringOutput
}

func New(ctx *pulumi.Context, name string, args *Args, opts ...pulumi.ResourceOption) (*Edge, error) {
	e := &Edge{}
	if err := ctx.RegisterComponentResource("serverless-react-postgres:azure:Edge", name, e, opts...); err != nil {
		return nil, err
	}
	parent := pulumi.Parent(e)

	client := authorization.GetClientConfigOutput(ctx, parent)

	accountNameRe := regexp.MustCompile("[^a-z0-9]")
	accountNameOutput := args.NamePrefix.ToStringOutput().ApplyT(func(prefix string) string {
		cleaned := accountNameRe.ReplaceAllString(strings.ToLower(prefix), "")
		if len(cleaned) > 20 {
			cleaned = cleaned[:20]
		}
		return cleaned
	}).(pulumi.StringOutput)

	storageAccount, err := storage.NewStorageAccount(ctx, fmt.Sprintf("%s-storage", name), &storage.StorageAccountArgs{
		AccountName:           accountNameOutput,
		ResourceGroupName:     args.ResourceGroupName,
		Location:              args.Location,
		Sku:                   &storage.SkuArgs{Name: pulumi.String(storage.SkuName_Standard_LRS)},
		Kind:                  pulumi.String(storage.KindStorageV2),
		AllowBlobPublicAccess: pulumi.Bool(true),
		MinimumTlsVersion:     storage.MinimumTlsVersion_TLS1_2,
		Tags:                  args.Tags,
	}, parent)
	if err != nil {
		return nil, err
	}

	staticWebsite, err := storage.NewStorageAccountStaticWebsite(ctx, fmt.Sprintf("%s-website", name), &storage.StorageAccountStaticWebsiteArgs{
		AccountName:       storageAccount.Name,
		ResourceGroupName: args.ResourceGroupName,
		IndexDocument:     pulumi.String("index.html"),
		Error404Document:  pulumi.String("index.html"),
	}, parent)
	if err != nil {
		return nil, err
	}

	codeContainer, err := storage.NewBlobContainer(ctx, fmt.Sprintf("%s-code", name), &storage.BlobContainerArgs{
		AccountName:       storageAccount.Name,
		ContainerName:     pulumi.String("function-code"),
		ResourceGroupName: args.ResourceGroupName,
		PublicAccess:      storage.PublicAccessNone,
	}, parent)
	if err != nil {
		return nil, err
	}

	siteFiles, err := walkFiles(args.WebsiteDistPath)
	if err != nil {
		return nil, err
	}
	for _, full := range siteFiles {
		rel, err := filepath.Rel(args.WebsiteDistPath, full)
		if err != nil {
			return nil, err
		}
		key := filepath.ToSlash(rel)
		safeKey := safeKeyRe.ReplaceAllString(key, "_")
		if _, err := storage.NewBlob(ctx, fmt.Sprintf("%s-site-%s", name, safeKey), &storage.BlobArgs{
			AccountName:       storageAccount.Name,
			ResourceGroupName: args.ResourceGroupName,
			ContainerName:     staticWebsite.ContainerName,
			BlobName:          pulumi.String(key),
			Source:            pulumi.NewFileAsset(full),
			ContentType:       pulumi.String(contentTypeFor(full)),
			Type:              storage.BlobTypeBlock,
		}, parent, pulumi.DependsOn([]pulumi.Resource{staticWebsite})); err != nil {
			return nil, err
		}
	}

	archive, err := buildArchive(args.ApiHandlerPath)
	if err != nil {
		return nil, err
	}
	functionZip, err := storage.NewBlob(ctx, fmt.Sprintf("%s-fn-zip", name), &storage.BlobArgs{
		AccountName:       storageAccount.Name,
		ResourceGroupName: args.ResourceGroupName,
		ContainerName:     codeContainer.Name,
		BlobName:          pulumi.String("functionapp.zip"),
		Source:            archive,
		ContentType:       pulumi.String("application/zip"),
		Type:              storage.BlobTypeBlock,
	}, parent)
	if err != nil {
		return nil, err
	}

	storageKeys := storage.ListStorageAccountKeysOutput(ctx, storage.ListStorageAccountKeysOutputArgs{
		AccountName:       storageAccount.Name,
		ResourceGroupName: args.ResourceGroupName,
	}, parent)
	storageConnectionString := pulumi.All(storageAccount.Name, storageKeys.Keys()).ApplyT(func(parts []interface{}) string {
		accountName := parts[0].(string)
		keys := parts[1].([]storage.StorageAccountKeyResponse)
		if len(keys) == 0 {
			return ""
		}
		return fmt.Sprintf("DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=core.windows.net",
			accountName, keys[0].Value)
	}).(pulumi.StringOutput)

	// Dedicated subnet for the Function App VNet integration (delegated to
	// Microsoft.Web/serverFarms). Landing-zone private subnets are general-purpose,
	// so we carve out a /26 in the 10.10.0.0/16 landing-zone VNet; adjust via
	// config if a collision is reported at deploy time.
	vnetName := args.VnetId.ToStringOutput().ApplyT(func(vid string) string {
		parts := strings.Split(vid, "/")
		return parts[len(parts)-1]
	}).(pulumi.StringOutput)
	subnetName := args.NamePrefix.ToStringOutput().ApplyT(func(prefix string) string {
		return prefix + "-web-sn"
	}).(pulumi.StringOutput)

	webSubnet, err := network.NewSubnet(ctx, fmt.Sprintf("%s-web-subnet", name), &network.SubnetArgs{
		SubnetName:         subnetName,
		ResourceGroupName:  args.ResourceGroupName,
		VirtualNetworkName: vnetName,
		AddressPrefix:      pulumi.String("10.10.240.0/26"),
		Delegations: network.DelegationArray{
			&network.DelegationArgs{
				Name:        pulumi.String("web"),
				ServiceName: pulumi.String("Microsoft.Web/serverFarms"),
			},
		},
	}, parent)
	if err != nil {
		return nil, err
	}

	plan, err := web.NewAppServicePlan(ctx, fmt.Sprintf("%s-plan", name), &web.AppServicePlanArgs{
		Kind:              pulumi.String("functionapp"),
		Location:          args.Location,
		ResourceGroupName: args.ResourceGroupName,
		Reserved:          pulumi.Bool(true),
		Sku: &web.SkuDescriptionArgs{
			Name: pulumi.String("FC1"),
			Tier: pulumi.String("FlexConsumption"),
		},
		Tags: args.Tags,
	}, parent)
	if err != nil {
		return nil, err
	}

	keyVaultReference := args.DatabaseSecretUri.ToStringOutput().ApplyT(func(uri string) string {
		return fmt.Sprintf("@Microsoft.KeyVault(SecretUri=%s)", uri)
	}).(pulumi.StringOutput)

	blobEndpoint := storageAccount.PrimaryEndpoints.ApplyT(func(ep storage.EndpointsResponse) string {
		return ep.Blob
	}).(pulumi.StringOutput)
	deploymentValue := pulumi.All(blobEndpoint, codeContainer.Name, functionZip.Name).ApplyT(func(parts []interface{}) string {
		return fmt.Sprintf("%s%s/%s", parts[0].(string), parts[1].(string), parts[2].(string))
	}).(pulumi.StringOutput)

	functionApp, err := web.NewWebApp(ctx, fmt.Sprintf("%s-fn", name), &web.WebAppArgs{
		Kind:              pulumi.String("functionapp,linux"),
		Location:          args.Location,
		ResourceGroupName: args.ResourceGroupName,
		ServerFarmId:      plan.ID(),
		HttpsOnly:         pulumi.Bool(true),
		Identity: &web.ManagedServiceIdentityArgs{
			Type: web.ManagedServiceIdentityTypeSystemAssigned,
		},
		VirtualNetworkSubnetId: webSubnet.ID().ToStringOutput(),
		FunctionAppConfig: &web.FunctionAppConfigArgs{
			Deployment: &web.FunctionsDeploymentArgs{
				Storage: &web.FunctionsDeploymentStorageArgs{
					Type:  pulumi.String(web.FunctionsDeploymentStorageTypeBlobContainer),
					Value: deploymentValue,
					Authentication: &web.FunctionsDeploymentAuthenticationArgs{
						Type:                               pulumi.String(web.AuthenticationTypeStorageAccountConnectionString),
						StorageAccountConnectionStringName: pulumi.String("AzureWebJobsStorage"),
					},
				},
			},
			Runtime: &web.FunctionsRuntimeArgs{
				Name:    pulumi.String(web.RuntimeNameNode),
				Version: pulumi.String("20"),
			},
			ScaleAndConcurrency: &web.FunctionsScaleAndConcurrencyArgs{
				InstanceMemoryMB:    args.FunctionMemoryMB,
				MaximumInstanceCount: pulumi.Int(100),
			},
		},
		SiteConfig: &web.SiteConfigArgs{
			LinuxFxVersion: pulumi.String("Node|20"),
			AppSettings: web.NameValuePairArray{
				&web.NameValuePairArgs{Name: pulumi.String("AzureWebJobsStorage"), Value: storageConnectionString},
				&web.NameValuePairArgs{Name: pulumi.String("FUNCTIONS_EXTENSION_VERSION"), Value: pulumi.String("~4")},
				&web.NameValuePairArgs{Name: pulumi.String("DATABASE_URL"), Value: keyVaultReference},
			},
		},
		Tags: args.Tags,
	}, parent, pulumi.DependsOn([]pulumi.Resource{functionZip}))
	if err != nil {
		return nil, err
	}

	const keyVaultSecretsUser = "4633458b-17de-408a-b874-0445c86b69e6"
	roleDefinitionId := client.SubscriptionId().ApplyT(func(sub string) string {
		return fmt.Sprintf("/subscriptions/%s/providers/Microsoft.Authorization/roleDefinitions/%s", sub, keyVaultSecretsUser)
	}).(pulumi.StringOutput)
	keyVaultScope := pulumi.All(client.SubscriptionId(), args.ResourceGroupName, args.KeyVaultName).ApplyT(func(parts []interface{}) string {
		return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.KeyVault/vaults/%s",
			parts[0], parts[1], parts[2])
	}).(pulumi.StringOutput)
	principalId := functionApp.Identity.ApplyT(func(id *web.ManagedServiceIdentityResponse) string {
		if id == nil {
			return ""
		}
		return id.PrincipalId
	}).(pulumi.StringOutput)

	if _, err := authorization.NewRoleAssignment(ctx, fmt.Sprintf("%s-fn-kv-access", name), &authorization.RoleAssignmentArgs{
		PrincipalId:      principalId,
		PrincipalType:    pulumi.String("ServicePrincipal"),
		RoleDefinitionId: roleDefinitionId,
		Scope:            keyVaultScope,
	}, parent); err != nil {
		return nil, err
	}

	edgeProfile, err := cdn.NewProfile(ctx, fmt.Sprintf("%s-edge", name), &cdn.ProfileArgs{
		ResourceGroupName: args.ResourceGroupName,
		Location:          pulumi.String("global"),
		Sku:               &cdn.SkuArgs{Name: pulumi.String(cdn.SkuName_Standard_AzureFrontDoor)},
		Tags:              args.Tags,
	}, parent)
	if err != nil {
		return nil, err
	}

	edgeEndpoint, err := cdn.NewAFDEndpoint(ctx, fmt.Sprintf("%s-endpoint", name), &cdn.AFDEndpointArgs{
		ProfileName:       edgeProfile.Name,
		ResourceGroupName: args.ResourceGroupName,
		Location:          pulumi.String("global"),
		EnabledState:      pulumi.String(cdn.EnabledStateEnabled),
		Tags:              args.Tags,
	}, parent)
	if err != nil {
		return nil, err
	}

	webOriginHost := storageAccount.PrimaryEndpoints.ApplyT(func(ep storage.EndpointsResponse) string {
		return strings.TrimSuffix(strings.TrimPrefix(strings.TrimPrefix(ep.Web, "https://"), "http://"), "/")
	}).(pulumi.StringOutput)

	websiteOriginGroup, err := cdn.NewAFDOriginGroup(ctx, fmt.Sprintf("%s-website-og", name), &cdn.AFDOriginGroupArgs{
		OriginGroupName:   pulumi.String("website-origins"),
		ProfileName:       edgeProfile.Name,
		ResourceGroupName: args.ResourceGroupName,
		LoadBalancingSettings: &cdn.LoadBalancingSettingsParametersArgs{
			SampleSize:                      pulumi.Int(4),
			SuccessfulSamplesRequired:       pulumi.Int(3),
			AdditionalLatencyInMilliseconds: pulumi.Int(50),
		},
		HealthProbeSettings: &cdn.HealthProbeParametersArgs{
			ProbePath:              pulumi.String("/"),
			ProbeRequestType:       cdn.HealthProbeRequestTypeHEAD,
			ProbeProtocol:          cdn.ProbeProtocolHttps,
			ProbeIntervalInSeconds: pulumi.Int(120),
		},
	}, parent)
	if err != nil {
		return nil, err
	}

	websiteOrigin, err := cdn.NewAFDOrigin(ctx, fmt.Sprintf("%s-website-origin", name), &cdn.AFDOriginArgs{
		OriginName:        pulumi.String("website"),
		OriginGroupName:   websiteOriginGroup.Name,
		ProfileName:       edgeProfile.Name,
		ResourceGroupName: args.ResourceGroupName,
		HostName:          webOriginHost,
		OriginHostHeader:  webOriginHost,
		HttpsPort:         pulumi.Int(443),
		EnabledState:      pulumi.String(cdn.EnabledStateEnabled),
	}, parent, pulumi.DependsOn([]pulumi.Resource{staticWebsite}))
	if err != nil {
		return nil, err
	}

	apiOriginGroup, err := cdn.NewAFDOriginGroup(ctx, fmt.Sprintf("%s-api-og", name), &cdn.AFDOriginGroupArgs{
		OriginGroupName:   pulumi.String("api-origins"),
		ProfileName:       edgeProfile.Name,
		ResourceGroupName: args.ResourceGroupName,
		LoadBalancingSettings: &cdn.LoadBalancingSettingsParametersArgs{
			SampleSize:                      pulumi.Int(4),
			SuccessfulSamplesRequired:       pulumi.Int(3),
			AdditionalLatencyInMilliseconds: pulumi.Int(50),
		},
		HealthProbeSettings: &cdn.HealthProbeParametersArgs{
			ProbePath:              pulumi.String("/api/health"),
			ProbeRequestType:       cdn.HealthProbeRequestTypeGET,
			ProbeProtocol:          cdn.ProbeProtocolHttps,
			ProbeIntervalInSeconds: pulumi.Int(120),
		},
	}, parent)
	if err != nil {
		return nil, err
	}

	apiOrigin, err := cdn.NewAFDOrigin(ctx, fmt.Sprintf("%s-api-origin", name), &cdn.AFDOriginArgs{
		OriginName:        pulumi.String("api"),
		OriginGroupName:   apiOriginGroup.Name,
		ProfileName:       edgeProfile.Name,
		ResourceGroupName: args.ResourceGroupName,
		HostName:          functionApp.DefaultHostName,
		OriginHostHeader:  functionApp.DefaultHostName,
		HttpsPort:         pulumi.Int(443),
		EnabledState:      pulumi.String(cdn.EnabledStateEnabled),
	}, parent)
	if err != nil {
		return nil, err
	}

	if _, err := cdn.NewRoute(ctx, fmt.Sprintf("%s-website-route", name), &cdn.RouteArgs{
		RouteName:         pulumi.String("website"),
		EndpointName:      edgeEndpoint.Name,
		ProfileName:       edgeProfile.Name,
		ResourceGroupName: args.ResourceGroupName,
		OriginGroup:       &cdn.ResourceReferenceArgs{Id: websiteOriginGroup.ID().ToStringOutput()},
		SupportedProtocols: pulumi.StringArray{
			pulumi.String(cdn.AFDEndpointProtocolsHttp),
			pulumi.String(cdn.AFDEndpointProtocolsHttps),
		},
		PatternsToMatch:     pulumi.StringArray{pulumi.String("/*")},
		ForwardingProtocol:  pulumi.String(cdn.ForwardingProtocolHttpsOnly),
		HttpsRedirect:       pulumi.String(cdn.HttpsRedirectEnabled),
		LinkToDefaultDomain: pulumi.String(cdn.LinkToDefaultDomainEnabled),
		EnabledState:        pulumi.String(cdn.EnabledStateEnabled),
	}, parent, pulumi.DependsOn([]pulumi.Resource{websiteOrigin})); err != nil {
		return nil, err
	}

	if _, err := cdn.NewRoute(ctx, fmt.Sprintf("%s-api-route", name), &cdn.RouteArgs{
		RouteName:         pulumi.String("api"),
		EndpointName:      edgeEndpoint.Name,
		ProfileName:       edgeProfile.Name,
		ResourceGroupName: args.ResourceGroupName,
		OriginGroup:       &cdn.ResourceReferenceArgs{Id: apiOriginGroup.ID().ToStringOutput()},
		SupportedProtocols: pulumi.StringArray{
			pulumi.String(cdn.AFDEndpointProtocolsHttp),
			pulumi.String(cdn.AFDEndpointProtocolsHttps),
		},
		PatternsToMatch:     pulumi.StringArray{pulumi.String("/api/*")},
		ForwardingProtocol:  pulumi.String(cdn.ForwardingProtocolHttpsOnly),
		HttpsRedirect:       pulumi.String(cdn.HttpsRedirectEnabled),
		LinkToDefaultDomain: pulumi.String(cdn.LinkToDefaultDomainEnabled),
		EnabledState:        pulumi.String(cdn.EnabledStateEnabled),
	}, parent, pulumi.DependsOn([]pulumi.Resource{apiOrigin})); err != nil {
		return nil, err
	}

	e.SiteUrl = edgeEndpoint.HostName.ApplyT(func(host string) string {
		return "https://" + host
	}).(pulumi.StringOutput)
	e.ApiUrl = edgeEndpoint.HostName.ApplyT(func(host string) string {
		return "https://" + host + "/api"
	}).(pulumi.StringOutput)
	e.FunctionAppName = functionApp.Name
	e.EndpointHostname = edgeEndpoint.HostName
	e.StorageAccountName = storageAccount.Name

	if err := ctx.RegisterResourceOutputs(e, pulumi.Map{
		"siteUrl":            e.SiteUrl,
		"apiUrl":             e.ApiUrl,
		"functionAppName":    e.FunctionAppName,
		"endpointHostname":   e.EndpointHostname,
		"storageAccountName": e.StorageAccountName,
	}); err != nil {
		return nil, err
	}
	return e, nil
}

Frequently asked questions

Do I need the Pulumi landing-zone stack first?
Yes. This blueprint reads the landing-zone networkId, privateSubnetIds, and secretsStore outputs through a StackReference so the database lives on the shared private network and the function secret lands under your cloud’s central secret store. Deploy the landing-zone family in the same cloud account first, then pulumi config set landingZoneStack <your-org>/landing-zone/dev.
Why a SPA plus API instead of server-side rendering?
Pure FaaS (Lambda, Azure Functions, Cloud Run functions) is a natural fit for JSON-returning handlers but awkward for SSR because streaming responses and adapter shims vary per cloud. Splitting the app into a static bundle plus one JSON endpoint keeps the backend trivial and lets the CDN cache the SPA.
Does this scale to zero?
The function scales to zero on all three clouds. The database tier varies - Aurora Serverless v2 on AWS can scale to 0 ACUs, so idle cost is storage plus backups only. Azure Database for PostgreSQL Flexible Server and Cloud SQL for PostgreSQL keep a minimum compute tier running; the blueprint picks the cheapest Burstable / db-f1-micro size and the cost + cleanup section shows how to stop the server manually.
How is the database password stored?
Pulumi generates a random.RandomPassword during pulumi up, writes it into {{secret_service}} under the landing-zone’s secrets-store scope, and injects the secret value into the function at cold start. The password never appears in stack outputs or in state files; pulumi config keeps the secret handle, not the value.
How do I add more API routes?
Edit api/src/handler.ts. The blueprint ships a one-route router for GET /api/random; add more case arms for new paths, run npm run build in api/, and rerun pulumi up. The Pulumi program repackages the bundle and redeploys the function.
How is the SPA bundled?
The website/ folder is a Vite + React project. Run npm install and npm run build in website/ before pulumi up; the Pulumi program uploads the built website/dist/ directory to the object-storage bucket and invalidates the CDN. If you want to test the SPA against the deployed API first, run npm run dev and point Vite at the Pulumi-exported apiUrl.
Can I use this without the landing-zone stack?
Yes, but you have to replace the StackReference block in the entrypoint with the network id, private subnets, and a secret-store scope you already control. The blueprint assumes the landing-zone values exist so the DB can land in a private subnet and the function can reach it through the cloud’s VPC integration.
What does this cost?
Idle cost on AWS is approximately storage plus the Aurora Serverless v2 minimum if ACUs are paused, plus the CloudFront distribution. Azure and GCP keep a small DB compute tier running continuously, so expect a low-double-digit monthly baseline even at zero traffic. pulumi destroy tears the whole stack down when you are done.