# Architecture gallery

> Page: https://docs.plotwork.dev/cloud-architecture/gallery. Index of every page: https://docs.plotwork.dev/llms.txt

Five full-size cloud and network architectures drawn in Plotwork, with the text that makes each one and the prompt that gets an agent to write something like it. Read them to see what the language does with real systems, then copy the text and change the names. It is for anyone drawing an environment with accounts, networks, tunnels, and several sites.

## TL;DR

| Question | Answer |
|---|---|
| What is here? | A disaster recovery setup that spans AWS and a managed host, a multi-account AWS network with numbered traffic flows, an edge network where tunnels reach private origins, an Azure hub and spoke with private DNS and a private database, and the same Azure workload shown by tenant, management group, subscription, and resource group. |
| What do they show that a small diagram does not? | Groups nested four deep, typed groups with their icons, provider icons on every service, [numbered steps](https://docs.plotwork.dev/writing-diagrams.md#7-numbered-steps) that say what happens in what order, and [animated lines](https://docs.plotwork.dev/writing-diagrams.md#8-animated-lines) for traffic that moves. |
| How do I use one? | Paste the prompt into your agent and change the names, or copy the text into a `diagram` fence. The text is the whole diagram. |
| Why is the text short? | The engine lays out the groups, routes the lines, sizes the boxes to fit the words, and draws both color schemes. You write what is in what and what talks to what. |
| Do the lines move on GitHub? | Animated lines move when the SVG is shown as an image in a browser, including in a README on GitHub. A PNG export, an email, or a reader that flattens SVG shows them still, and motion stops for a reader who asks for reduced motion. |

## 1. Hybrid disaster recovery

A production site and a disaster recovery site, each with a managed host and an AWS account behind it. Staff and customers connect over site-to-site VPN, and the two sites replicate over a private WAN.

What to notice:

- Groups nest four deep (site, host, power environment, and AWS account) and each has a `type=` that picks its icon and tone.
- The two sites sit side by side because both are reached from the customer gateways. Nothing in the text says where to put them.
- Only the lines that carry traffic move (`animate=on`): the VPN tunnels and the replication. The rest stays still, so the eye finds the data in flight.

Ask your agent:

```text
Use Plotwork to draw a hybrid disaster recovery design. A customer's users and on-premises resources connect through customer gateways over site-to-site VPN to a production site and a disaster recovery site. Each site has a managed host with a VPN gateway, an isolated power environment running IBM i partitions with flash storage, and WAN routers, plus an AWS account with Direct Connect to application servers and object storage. The two sites replicate over a private multipath WAN. Engineering support centres reach both sites through a remote management gateway over VPN. Animate the VPN and replication lines.
```

**Diagram source**

```diagram
flow "Hybrid disaster recovery across AWS and a managed host" desc="A customer reaches a production site and a disaster recovery site over site-to-site VPN. Each site runs IBM i workloads in an isolated managed environment, linked to AWS over Direct Connect, and the two sites replicate over a private WAN." direction=down

group customer "Customer" type=on-premises {
  node users person "Production, remote and development users"
  node onprem service "On-premises resources"
  node cgw gateway "Customer gateways" icon=aws:vpc-customer-gateway sub="Site-to-site VPN"
}

group support "Engineering support centres" type=management-subnet {
  node engineers person "IBM i, AWS, network, NOC and OS engineers"
  node mgmt gateway "Remote management gateway" icon=lucide:router
  node platform service "Management platform" sub="Monitoring, tickets, runbooks"
}

group east "Production site" type=region {
  group east-host "Managed host, US East" type=cloud-boundary {
    node east-vpn gateway "VPN gateway" icon=aws:vpc-vpn-gateway
    group east-power "Power environment, isolated VRF" type=isolated-subnet {
      node east-ibm service "IBM i partitions" sub="Production workloads"
      node east-flash storage "Flash storage"
    }
    node east-wan gateway "WAN routers" icon=lucide:router
  }
  group east-aws "AWS, US East" type=account provider=aws {
    node east-dc gateway "Direct Connect" icon=aws:direct-connect sub="Dedicated, under 2 ms"
    group east-az "Availability zones" type=zone {
      node east-app service "Application servers" icon=aws:ec2
      node east-objects storage "Object storage" icon=aws:s3
    }
  }
}

group west "Disaster recovery site" type=region {
  group west-host "Managed host, US West" type=cloud-boundary {
    node west-vpn gateway "VPN gateway" icon=aws:vpc-vpn-gateway
    group west-power "Power environment, isolated VRF" type=isolated-subnet {
      node west-ibm service "IBM i partitions" sub="Standby, replicated"
      node west-flash storage "Flash storage"
    }
    node west-wan gateway "WAN routers" icon=lucide:router
  }
  group west-aws "AWS, US West" type=account provider=aws {
    node west-dc gateway "Direct Connect" icon=aws:direct-connect sub="Dedicated, under 2 ms"
    group west-az "Availability zones" type=zone {
      node west-app service "Application servers" icon=aws:ec2 sub="Stopped until failover"
      node west-objects storage "Object storage" icon=aws:s3
    }
  }
}

node wan gateway "Private WAN" icon=lucide:network sub="Multipath, between sites"

users -> cgw
onprem -> cgw
cgw -> east-vpn : "VPN" animate=on
cgw -> west-vpn : "VPN" animate=on

engineers -> mgmt
mgmt -> platform
mgmt -> east-vpn : "Management VPN" animate=on
mgmt -> west-vpn : "Management VPN" animate=on

east-vpn -> east-ibm
west-vpn -> west-ibm
east-ibm -> east-flash
west-ibm -> west-flash

east-ibm -> east-wan
west-ibm -> west-wan
east-wan <-> wan : "VTL and SAN replication" animate=on
west-wan <-> wan : "VTL and SAN replication" animate=on

east-wan -> east-dc
west-wan -> west-dc
east-dc -> east-app
west-dc -> west-app
east-app -> east-objects
west-app -> west-objects
```

*Picture: Hybrid disaster recovery across AWS and a managed host: 21 nodes, 22 connections. A customer reaches a production site and a disaster recovery site over site-to-site VPN. Each site runs IBM i workloads in an isolated managed environment, linked to AWS over Direct Connect, and the two sites replicate over a private WAN.*

**Text description**

**Nodes:**

- Private WAN (Multipath, between sites), gateway
- On premises: Customer
  - Production, remote and development users, person
  - On-premises resources, service
  - Customer gateways (Site-to-site VPN), gateway
- Management subnet: Engineering support centres
  - IBM i, AWS, network, NOC and OS engineers, person
  - Remote management gateway, gateway
  - Management platform (Monitoring, tickets, runbooks), service
- Region: Production site
  - Cloud boundary: Managed host, US East
    - VPN gateway, gateway
    - WAN routers, gateway
    - Isolated subnet: Power environment, isolated VRF
      - IBM i partitions (Production workloads), service
      - Flash storage, storage
  - Account: AWS, US East
    - Direct Connect (Dedicated, under 2 ms), gateway
    - Availability zone: Availability zones
      - Application servers, service
      - Object storage, storage
- Region: Disaster recovery site
  - Cloud boundary: Managed host, US West
    - VPN gateway, gateway
    - WAN routers, gateway
    - Isolated subnet: Power environment, isolated VRF
      - IBM i partitions (Standby, replicated), service
      - Flash storage, storage
  - Account: AWS, US West
    - Direct Connect (Dedicated, under 2 ms), gateway
    - Availability zone: Availability zones
      - Application servers (Stopped until failover), service
      - Object storage, storage

**Connections:**

- Production, remote and development users to Customer gateways
- On-premises resources to Customer gateways
- Customer gateways to VPN gateway: VPN
- Customer gateways to VPN gateway: VPN
- IBM i, AWS, network, NOC and OS engineers to Remote management gateway
- Remote management gateway to Management platform
- Remote management gateway to VPN gateway: Management VPN
- Remote management gateway to VPN gateway: Management VPN
- VPN gateway to IBM i partitions
- VPN gateway to IBM i partitions
- IBM i partitions to Flash storage
- IBM i partitions to Flash storage
- IBM i partitions to WAN routers
- IBM i partitions to WAN routers
- WAN routers with Private WAN: VTL and SAN replication
- WAN routers with Private WAN: VTL and SAN replication
- WAN routers to Direct Connect
- WAN routers to Direct Connect
- Direct Connect to Application servers
- Direct Connect to Application servers
- Application servers to Object storage
- Application servers to Object storage

## 2. Multi-account AWS network with numbered flows

Three AWS accounts joined by a Transit Gateway: networking, shared services, and an application account. The numbers on the lines say what happens, in order, from a visitor's request to the outbound traffic of the application.

What to notice:

- `numbered=on` in the header puts a numbered badge on every line in the order the lines are written. The text description reads "1. External users to Internet gateway: Browse the site" and so on, so the order survives for someone who cannot see the picture.
- `provider=aws` in the header sets the icons of the typed groups, and each service carries its own `icon=aws:` name.
- A line can skip a number with `step=off`, or set one with `step=12`, when a flow has side paths.

Ask your agent:

```text
Use Plotwork to draw a multi-account AWS network with numbered traffic flows. A networking account holds a DMZ VPC with an internet gateway, a Direct Connect connection, a Transit Gateway, and an egress VPC with a NAT gateway. A shared services account holds Managed Microsoft AD and a management host in private subnets. An application account has an Elastic Load Balancing tier in public subnets and an auto scaling group in private subnets. Number the steps: external users reach the site through the internet gateway and the Transit Gateway to the load balancer, then the application authenticates users against the directory. Corporate staff connect over Direct Connect through the Transit Gateway to the management host. The application sends outbound updates through the NAT gateway. Animate the lines that carry user traffic.
```

**Diagram source**

```diagram
flow "Multi-account AWS network with numbered traffic flows" desc="Internet users and corporate staff reach applications through a networking account. A Transit Gateway joins the networking, shared services and application accounts, and the numbers show each hop in order." direction=down numbered=on provider=aws

node ext person "External users"
node corp person "Corporate users"

group apps "Application account A" type=account {
  group apps-vpc "Application VPC" type=network {
    group apps-public "Public subnets, 3 zones" type=public-subnet {
      node elb gateway "Elastic Load Balancing" icon=aws:elb
    }
    group apps-private "Private subnets, 3 zones" type=private-subnet {
      node app service "Application instances" icon=aws:ec2 sub="Auto scaling group"
    }
  }
}

group networking "Networking account" type=account {
  group dmz "DMZ VPC" type=network {
    node igw gateway "Internet gateway" icon=aws:internet-gateway
  }
  node dx gateway "Direct Connect" icon=aws:direct-connect sub="Dedicated connection"
  node tgw gateway "Transit Gateway" icon=aws:transit-gateway sub="Routes every account"
  group egress "Networking egress VPC" type=network {
    node nat gateway "NAT gateway" icon=aws:nat-gateway sub="One per zone"
  }
}

group shared "Shared services account" type=account {
  group shared-vpc "Shared services VPC" type=network {
    group shared-private "Private subnets, 3 zones" type=private-subnet {
      node ad service "Managed Microsoft AD" icon=aws:directory-service-managed-microsoft-ad
      node host service "Management host" icon=aws:ec2
    }
  }
}

ext -> igw : "Browse the site" animate=on
igw -> tgw : "Route to the application account"
tgw -> elb : "HTTPS"
elb -> app : "Forward the request" animate=on
app -> ad : "Authenticate users" role=identity
corp -> dx : "Corporate staff connect"
dx -> tgw
tgw -> host : "Administer"
app -> nat : "Outbound updates" animate=on
```

*Picture: Multi-account AWS network with numbered traffic flows: 10 nodes, 9 connections. Internet users and corporate staff reach applications through a networking account. A Transit Gateway joins the networking, shared services and application accounts, and the numbers show each hop in order.*

**Text description**

**Nodes:**

- External users, person
- Corporate users, person
- Account: Application account A
  - Virtual network: Application VPC
    - Public subnet: Public subnets, 3 zones
      - Elastic Load Balancing, gateway
    - Private subnet: Private subnets, 3 zones
      - Application instances (Auto scaling group), service
- Account: Networking account
  - Direct Connect (Dedicated connection), gateway
  - Transit Gateway (Routes every account), gateway
  - Virtual network: DMZ VPC
    - Internet gateway, gateway
  - Virtual network: Networking egress VPC
    - NAT gateway (One per zone), gateway
- Account: Shared services account
  - Virtual network: Shared services VPC
    - Private subnet: Private subnets, 3 zones
      - Managed Microsoft AD, service
      - Management host, service

**Connections:**

- 1. External users to Internet gateway: Browse the site
- 2. Internet gateway to Transit Gateway: Route to the application account
- 3. Transit Gateway to Elastic Load Balancing: HTTPS
- 4. Elastic Load Balancing to Application instances: Forward the request
- 5. Application instances to Managed Microsoft AD (identity line): Authenticate users
- 6. Corporate users to Direct Connect: Corporate staff connect
- 7. Direct Connect to Transit Gateway
- 8. Transit Gateway to Management host: Administer
- 9. Application instances to NAT gateway: Outbound updates

## 3. An edge network with tunnels to private origins

Visitors and remote staff reach the nearest edge server. Every server runs every service, and a small agent next to each origin keeps an outbound-only tunnel open, so nothing behind it has a public address.

What to notice:

- The services sit in one group, "Every service, on every server", in two chains: application protection on the left and zero trust on the right. Each chain reads top to bottom in the order traffic meets it.
- The tunnels are the only lines that move, so the picture shows which traffic crosses the boundary into private space.
- The icons come from the `cloudflare:` set, and the nodes without a product icon keep the default glyph of their type.

Ask your agent:

```text
Use Plotwork to draw an edge network where tunnels reach private origins. Visitors on a public network reach an edge server. Remote staff run a device agent that opens an encrypted tunnel to a second edge server. Inside the edge network a group called "Every service, on every server" holds two chains: DDoS protection, an L3 and L4 firewall, a web application firewall, bot management, load balancing and a CDN; and zero trust network access, a secure web gateway, data loss prevention, a cloud access security broker and remote browser isolation. An IaaS group with an application and a data centre with a private network of two databases each run a cloudflared agent, and a tunnel connects each to the edge network. Animate the tunnels.
```

**Diagram source**

```diagram
flow "Cloudflare Anycast network" desc="Visitors and remote staff reach the nearest Cloudflare server. Every server runs every service, and lightweight tunnel agents carry traffic to applications in a cloud and to private networks in a data centre, so no origin has a public address." direction=down

group public "Public network" type=client-side {
  node visitor person "Visitors" sub="Browser and mobile"
}

group home "Home office" type=client-side {
  node staff person "Remote staff"
  node agent service "Device agent" icon=cloudflare:access sub="Zero trust client"
}

group internet "Internet" type=cloud-boundary {
  group anycast "Cloudflare Anycast network" type=edge {
    node edge-a service "Edge server" icon=lucide:server sub="Nearest data centre"
    node edge-b service "Edge server" icon=lucide:server sub="Nearest data centre"
    group services "Every service, on every server" type=security {
      node ddos service "DDoS protection"
      node fw service "L3 and L4 firewall"
      node waf service "Web application firewall" icon=cloudflare:waf
      node bots service "Bot management"
      node lb service "Load balancing"
      node cdn service "CDN and cache" icon=cloudflare:cdn
      node ztna service "Zero trust network access" icon=cloudflare:access
      node swg service "Secure web gateway"
      node dlp service "Data loss prevention"
      node casb service "Cloud access security broker"
      node rbi service "Remote browser isolation"
    }
  }
}

group iaas "IaaS" type=cloud {
  node cf-iaas service "cloudflared" icon=cloudflare:cloud sub="Outbound-only tunnel"
  node app service "Application"
}

group dc "Data centre" type=on-premises {
  node cf-dc service "cloudflared" icon=cloudflare:cloud sub="Outbound-only tunnel"
  group private "Private network" type=private-subnet {
    node db1 database "Database"
    node db2 database "Database"
  }
}

visitor -> edge-a : "HTTPS"
agent -> edge-b : "Encrypted tunnel" animate=on
staff -> agent

edge-a -> ddos
ddos -> fw
fw -> waf
waf -> bots
bots -> lb
lb -> cdn

edge-b -> ztna
ztna -> swg
swg -> dlp
dlp -> casb
casb -> rbi

cdn -> cf-iaas : "Tunnel" animate=on
cf-iaas -> app
rbi -> cf-dc : "Tunnel" animate=on
cf-dc -> db1
cf-dc -> db2
```

*Picture: Cloudflare Anycast network: 21 nodes, 19 connections. Visitors and remote staff reach the nearest Cloudflare server. Every server runs every service, and lightweight tunnel agents carry traffic to applications in a cloud and to private networks in a data centre, so no origin has a public address.*

**Text description**

**Nodes:**

- Client side: Public network
  - Visitors (Browser and mobile), person
- Client side: Home office
  - Remote staff, person
  - Device agent (Zero trust client), service
- Cloud boundary: Internet
  - Edge: Cloudflare Anycast network
    - Edge server (Nearest data centre), service
    - Edge server (Nearest data centre), service
    - Security: Every service, on every server
      - DDoS protection, service
      - L3 and L4 firewall, service
      - Web application firewall, service
      - Bot management, service
      - Load balancing, service
      - CDN and cache, service
      - Zero trust network access, service
      - Secure web gateway, service
      - Data loss prevention, service
      - Cloud access security broker, service
      - Remote browser isolation, service
- Cloud: IaaS
  - cloudflared (Outbound-only tunnel), service
  - Application, service
- On premises: Data centre
  - cloudflared (Outbound-only tunnel), service
  - Private subnet: Private network
    - Database, database
    - Database, database

**Connections:**

- Visitors to Edge server: HTTPS
- Device agent to Edge server: Encrypted tunnel
- Remote staff to Device agent
- Edge server to DDoS protection
- DDoS protection to L3 and L4 firewall
- L3 and L4 firewall to Web application firewall
- Web application firewall to Bot management
- Bot management to Load balancing
- Load balancing to CDN and cache
- Edge server to Zero trust network access
- Zero trust network access to Secure web gateway
- Secure web gateway to Data loss prevention
- Data loss prevention to Cloud access security broker
- Cloud access security broker to Remote browser isolation
- CDN and cache to cloudflared: Tunnel
- cloudflared to Application
- Remote browser isolation to cloudflared: Tunnel
- cloudflared to Database
- cloudflared to Database

## 4. Azure hub and spoke with private DNS and a private database

A React frontend on a Web App and an API on a Function App share one App Service plan in a workload spoke. Users arrive through Front Door and Private Link, the API reaches PostgreSQL Flexible Server, Key Vault, and Storage over private addresses, and a hub firewall and DNS resolver serve every spoke.

What to notice:

- The spoke and the hub are two `type=network` groups with subnets inside them, each with its address range in `sub=`. A subnet delegated to a service (App Service, PostgreSQL) says so in its name.
- The App Service plan is a group of its own (`type=service-plan`) around the two apps that run on it.
- The numbered steps follow one request from the browser to the database and to the vault. The lines that only keep the platform running (egress through the firewall, DNS lookups, telemetry) use `step=off`, so they are drawn without taking a number.
- Four private DNS zones sit together, linked to the hub and the spoke, and the resolver in the hub answers for all of them.

Ask your agent:

```text
Use Plotwork to draw an Azure hub and spoke network with private DNS. Users reach a Front Door Premium with WAF, which reaches a Web App (React frontend) through a private endpoint. The Web App and a Function App (the API) run on one zone-redundant App Service plan in an integration subnet delegated to App Service, in a workload spoke VNet. The Function App reads secrets from Key Vault and files from a Storage account through private endpoints in a separate subnet, and queries PostgreSQL Flexible Server with private access in a subnet delegated to PostgreSQL. A hub VNet holds an Azure Firewall for egress and a Private DNS resolver, which answers for four private DNS zones: azurewebsites, vaultcore, blob, and postgres. Application Insights and a Log Analytics workspace collect telemetry. Number the steps of one request and animate the Private Link, the API call and the database lines.
```

**Diagram source**

```diagram
flow "Azure hub and spoke with private DNS and a private database" desc="A React frontend on a Web App and an API on a Function App share one App Service plan in a workload spoke. Users arrive through Front Door and Private Link, the API reaches PostgreSQL Flexible Server, Key Vault and Storage over private addresses, and a hub firewall and DNS resolver serve every spoke." direction=down numbered=on provider=azure

node users client "Users" sub="Browsers"

node afd gateway "Front Door Premium" icon=azure:front-door sub="WAF and global entry"

group spoke "Workload spoke VNet" type=network sub="10.1.0.0/16" {
  group entry "Frontend endpoint subnet" type=isolated-subnet sub="10.1.2.0/25" {
    node pe-web gateway "Private endpoint: Web App" icon=azure:private-endpoints
  }
  group integration "Integration subnet, delegated to App Service" type=private-subnet sub="10.1.1.0/24" {
    group plan "App Service plan" type=service-plan icon=azure:app-service-plans sub="Premium, zone redundant" {
      node web service "Web App" icon=azure:app-service sub="React frontend"
      node func function "Function App" icon=azure:functions sub="API"
    }
  }
  group endpoints "Backend endpoint subnet" type=isolated-subnet sub="10.1.2.128/25" {
    node pe-kv gateway "Private endpoint: Key Vault" icon=azure:private-endpoints
    node pe-st gateway "Private endpoint: Storage" icon=azure:private-endpoints
  }
  group data "Database subnet, delegated to PostgreSQL" type=isolated-subnet sub="10.1.3.0/24" {
    node pg database "PostgreSQL Flexible Server" icon=azure:database-for-postgresql sub="Private access, zone redundant HA"
  }
}

group platform "Platform services, public access disabled" type=data-tier {
  node kv secret "Key Vault" icon=azure:key-vault sub="Secrets and keys"
  node st storage "Storage account" icon=azure:storage-accounts sub="Blob files"
}

group hub "Hub VNet" type=network sub="10.0.0.0/16" {
  group fw-subnet "AzureFirewallSubnet" type=subnet sub="10.0.0.0/26" {
    node fw gateway "Azure Firewall" icon=azure:firewall sub="Egress rules and DNS proxy"
  }
  group dns-subnet "DNS resolver subnet" type=subnet sub="10.0.1.0/28" {
    node resolver gateway "Private DNS resolver" icon=azure:dns-private-resolver
  }
}

group zones "Private DNS zones, linked to the hub and the spoke" type=data-tier {
  node dns-web service "privatelink.azurewebsites.net" icon=azure:dns-zones
  node dns-kv service "privatelink.vaultcore.azure.net" icon=azure:dns-zones
  node dns-st service "privatelink.blob.core.windows.net" icon=azure:dns-zones
  node dns-pg service "private.postgres.database.azure.com" icon=azure:dns-zones
}

group monitoring "Monitoring" type=observability {
  node insights service "Application Insights" icon=azure:application-insights
  node logs service "Log Analytics workspace" icon=azure:log-analytics
}

users -> afd : "HTTPS"
afd -> pe-web : "Private Link origin" animate=on
pe-web -> web : "Frontend assets"
web -> func : "API calls over VNet integration" animate=on
func -> pg : "SQL over the private subnet" animate=on
func -> pe-kv : "Secrets"
pe-kv -> kv
func -> pe-st : "Files"
pe-st -> st

func -> fw : "Egress through the hub" step=off
fw -> resolver : "DNS proxy" step=off
resolver -> dns-web step=off
resolver -> dns-kv step=off
resolver -> dns-st step=off
resolver -> dns-pg step=off

func -> insights step=off
insights -> logs step=off
```

*Picture: Azure hub and spoke with private DNS and a private database: 18 nodes, 17 connections. A React frontend on a Web App and an API on a Function App share one App Service plan in a workload spoke. Users arrive through Front Door and Private Link, the API reaches PostgreSQL Flexible Server, Key Vault and Storage over private addresses, and a hub firewall and DNS resolver serve every spoke.*

**Text description**

**Nodes:**

- Users (Browsers), client
- Front Door Premium (WAF and global entry), gateway
- Virtual network: Workload spoke VNet (10.1.0.0/16)
  - Isolated subnet: Frontend endpoint subnet (10.1.2.0/25)
    - Private endpoint: Web App, gateway
  - Private subnet: Integration subnet, delegated to App Service (10.1.1.0/24)
    - Service plan: App Service plan (Premium, zone redundant)
      - Web App (React frontend), service
      - Function App (API), function
  - Isolated subnet: Backend endpoint subnet (10.1.2.128/25)
    - Private endpoint: Key Vault, gateway
    - Private endpoint: Storage, gateway
  - Isolated subnet: Database subnet, delegated to PostgreSQL (10.1.3.0/24)
    - PostgreSQL Flexible Server (Private access, zone redundant HA), database
- Data tier: Platform services, public access disabled
  - Key Vault (Secrets and keys), secret
  - Storage account (Blob files), storage
- Virtual network: Hub VNet (10.0.0.0/16)
  - Subnet: AzureFirewallSubnet (10.0.0.0/26)
    - Azure Firewall (Egress rules and DNS proxy), gateway
  - Subnet: DNS resolver subnet (10.0.1.0/28)
    - Private DNS resolver, gateway
- Data tier: Private DNS zones, linked to the hub and the spoke
  - privatelink.azurewebsites.net, service
  - privatelink.vaultcore.azure.net, service
  - privatelink.blob.core.windows.net, service
  - private.postgres.database.azure.com, service
- Observability: Monitoring
  - Application Insights, service
  - Log Analytics workspace, service

**Connections:**

- 1. Users to Front Door Premium: HTTPS
- 2. Front Door Premium to Private endpoint: Web App: Private Link origin
- 3. Private endpoint: Web App to Web App: Frontend assets
- 4. Web App to Function App: API calls over VNet integration
- 5. Function App to PostgreSQL Flexible Server: SQL over the private subnet
- 6. Function App to Private endpoint: Key Vault: Secrets
- 7. Private endpoint: Key Vault to Key Vault
- 8. Function App to Private endpoint: Storage: Files
- 9. Private endpoint: Storage to Storage account
- Function App to Azure Firewall: Egress through the hub
- Azure Firewall to Private DNS resolver: DNS proxy
- Private DNS resolver to privatelink.azurewebsites.net
- Private DNS resolver to privatelink.vaultcore.azure.net
- Private DNS resolver to privatelink.blob.core.windows.net
- Private DNS resolver to private.postgres.database.azure.com
- Function App to Application Insights
- Application Insights to Log Analytics workspace

## 5. Azure landing zone: tenant, subscriptions, and resource groups

The Azure workload of the previous example, drawn again with where each part is owned. One Microsoft Entra tenant signs users in with Conditional Access and multifactor authentication (MFA), a tree of management groups applies Azure Policy, and three subscriptions (connectivity, management, and workload) hold the resource groups that contain every resource.

What to notice:

- The nesting is the ownership chain: tenant root management group, then a management group, then a subscription (`type=account`), then a resource group (`type=resource-group`), then the resource. Each level has its own icon from the Azure set.
- The Entra tenant is a group of its own (`type=identity-boundary`) with the sign-in services in it: Entra ID, Conditional Access with MFA, Privileged Identity Management, and app registrations.
- The first three numbers are the sign-in: the user signs in, Conditional Access evaluates it, and a token is issued before the request goes on to Front Door. The API then uses a managed identity, with no stored credentials, to read secrets and to connect to PostgreSQL with an Entra token.
- Private endpoints sit in the resource group of the thing they protect, next to Key Vault, which is where Azure puts them by default.
- Platform lines (egress, DNS, telemetry, policy) use `step=off`, so the numbers stay on the request path.

Ask your agent:

```text
Use Plotwork to draw an Azure landing zone with the ownership shown. Put a Microsoft Entra tenant at the top with Entra ID, Conditional Access and MFA, Privileged Identity Management, and app registrations. Below it a tenant root management group holds Azure Policy and Defender for Cloud and two child management groups: landing zones, with a workload production subscription, and platform, with a connectivity subscription and a management subscription. In the workload subscription put three resource groups: spoke network (a spoke VNet with a private endpoint subnet for the Web App), app (an App Service plan with a React Web App and a Function App API, plus a managed identity), and data (a private endpoint for Key Vault, Key Vault with RBAC, and PostgreSQL Flexible Server with Entra authentication). In the connectivity subscription put a hub network resource group (Azure Firewall and a Private DNS resolver) and a private DNS resource group (zones for azurewebsites, vaultcore and postgres). In the management subscription put a monitoring resource group with Application Insights and a Log Analytics workspace. Number one request: the user signs in, Conditional Access evaluates it, a token is issued, Front Door reaches the Web App by Private Link, the Web App calls the Function App with the bearer token, the Function App's managed identity reads Key Vault and connects to PostgreSQL. Animate the Private Link, the API call and the database connection.
```

**Diagram source**

```diagram
flow "Azure landing zone: tenant, subscriptions and resource groups" desc="The same hub and spoke workload shown with where everything is owned: one Microsoft Entra tenant signs users in with Conditional Access and MFA, a management group tree applies policy, and three subscriptions (connectivity, management and workload) hold the resource groups that contain each resource." direction=down numbered=on provider=azure

node users client "Users" sub="Browsers"

group tenant "Microsoft Entra tenant" type=identity-boundary icon=azure:entra-id {
  node entra identity "Entra ID" icon=azure:entra-id sub="Users, groups and sign-in"
  node mfa service "Conditional Access and MFA" icon=azure:conditional-access sub="Require MFA and a compliant device"
  node pim service "Privileged Identity Management" icon=lucide:shield-check sub="Just-in-time admin roles"
  node appreg service "App registrations" icon=azure:app-registrations sub="Frontend and API"
}

node afd gateway "Front Door Premium" icon=azure:front-door sub="WAF and global entry"

group root "Tenant root management group" type=cloud icon=azure:management-groups {
  node policy service "Azure Policy" icon=azure:policy sub="Allowed regions, required tags, private only"
  node defender service "Defender for Cloud" icon=azure:defender-for-cloud sub="Secure score and threat protection"

  group landing "Landing zones management group" type=cloud icon=azure:management-groups {
    group sub-prod "Workload production subscription" type=account icon=azure:subscriptions {
      group rg-net "rg-spoke-network-prod" type=resource-group icon=azure:resource-groups {
        group spoke "Spoke VNet 10.1.0.0/16" type=network {
          group pe-subnet "Private endpoint subnet" type=isolated-subnet {
            node pe-web gateway "Private endpoint: Web App" icon=azure:private-endpoints
          }
        }
      }
      group rg-app "rg-app-prod" type=resource-group icon=azure:resource-groups {
        group plan "App Service plan" type=service-plan icon=azure:app-service-plans sub="Premium, zone redundant" {
          node web service "Web App" icon=azure:app-service sub="React frontend"
          node func function "Function App" icon=azure:functions sub="API"
        }
        node mi service "Managed identity" icon=azure:managed-identities sub="No stored credentials"
      }
      group rg-data "rg-data-prod" type=resource-group icon=azure:resource-groups {
        node pe-kv gateway "Private endpoint: Key Vault" icon=azure:private-endpoints
        node kv secret "Key Vault" icon=azure:key-vault sub="RBAC, no access policies"
        node pg database "PostgreSQL Flexible Server" icon=azure:database-for-postgresql sub="Private access, Entra authentication"
      }
    }
  }

  group platform "Platform management group" type=cloud icon=azure:management-groups {
    group sub-conn "Connectivity subscription" type=account icon=azure:subscriptions {
      group rg-hub "rg-hub-network" type=resource-group icon=azure:resource-groups {
        group hub "Hub VNet 10.0.0.0/16" type=network {
          node fw gateway "Azure Firewall" icon=azure:firewall sub="Egress and DNS proxy"
          node resolver gateway "Private DNS resolver" icon=azure:dns-private-resolver
        }
      }
      group rg-dns "rg-private-dns" type=resource-group icon=azure:resource-groups {
        node dns-pg service "private.postgres.database.azure.com" icon=azure:dns-zones
        node dns-kv service "privatelink.vaultcore.azure.net" icon=azure:dns-zones
        node dns-web service "privatelink.azurewebsites.net" icon=azure:dns-zones
      }
    }
    group sub-mgmt "Management subscription" type=account icon=azure:subscriptions {
      group rg-mon "rg-monitoring" type=resource-group icon=azure:resource-groups {
        node insights service "Application Insights" icon=azure:application-insights
        node logs service "Log Analytics workspace" icon=azure:log-analytics
      }
    }
  }
}

users -> entra : "Sign in" role=identity
entra -> mfa : "Evaluate Conditional Access"
mfa -> afd : "Token issued, request continues"
afd -> pe-web : "Private Link origin" animate=on
pe-web -> web : "Frontend"
web -> func : "API call with the bearer token" animate=on
func -> mi : "Request a token"
mi -> pe-kv : "Read secrets, with RBAC" role=identity
pe-kv -> kv
mi -> pg : "Connect with an Entra token" animate=on

entra -> appreg : "Registers the apps" step=off
entra -> pim : "Elevates admins" step=off
policy -> defender : "Policy results feed the score" step=off
func -> fw : "Egress through the hub" step=off
fw -> resolver : "DNS proxy" step=off
resolver -> dns-pg step=off
resolver -> dns-kv step=off
resolver -> dns-web step=off
func -> insights : "Telemetry" step=off
insights -> logs step=off
```

*Picture: Azure landing zone: tenant, subscriptions and resource groups: 22 nodes, 20 connections. The same hub and spoke workload shown with where everything is owned: one Microsoft Entra tenant signs users in with Conditional Access and MFA, a management group tree applies policy, and three subscriptions (connectivity, management and workload) hold the resource groups that contain each resource.*

**Text description**

**Nodes:**

- Users (Browsers), client
- Front Door Premium (WAF and global entry), gateway
- Identity: Microsoft Entra tenant
  - Entra ID (Users, groups and sign-in), identity
  - Conditional Access and MFA (Require MFA and a compliant device), service
  - Privileged Identity Management (Just-in-time admin roles), service
  - App registrations (Frontend and API), service
- Cloud: Tenant root management group
  - Azure Policy (Allowed regions, required tags, private only), service
  - Defender for Cloud (Secure score and threat protection), service
  - Cloud: Landing zones management group
    - Account: Workload production subscription
      - Resource group: rg-spoke-network-prod
        - Virtual network: Spoke VNet 10.1.0.0/16
          - Isolated subnet: Private endpoint subnet
            - Private endpoint: Web App, gateway
      - Resource group: rg-app-prod
        - Managed identity (No stored credentials), service
        - Service plan: App Service plan (Premium, zone redundant)
          - Web App (React frontend), service
          - Function App (API), function
      - Resource group: rg-data-prod
        - Private endpoint: Key Vault, gateway
        - Key Vault (RBAC, no access policies), secret
        - PostgreSQL Flexible Server (Private access, Entra authentication), database
  - Cloud: Platform management group
    - Account: Connectivity subscription
      - Resource group: rg-hub-network
        - Virtual network: Hub VNet 10.0.0.0/16
          - Azure Firewall (Egress and DNS proxy), gateway
          - Private DNS resolver, gateway
      - Resource group: rg-private-dns
        - private.postgres.database.azure.com, service
        - privatelink.vaultcore.azure.net, service
        - privatelink.azurewebsites.net, service
    - Account: Management subscription
      - Resource group: rg-monitoring
        - Application Insights, service
        - Log Analytics workspace, service

**Connections:**

- 1. Users to Entra ID (identity line): Sign in
- 2. Entra ID to Conditional Access and MFA: Evaluate Conditional Access
- 3. Conditional Access and MFA to Front Door Premium: Token issued, request continues
- 4. Front Door Premium to Private endpoint: Web App: Private Link origin
- 5. Private endpoint: Web App to Web App: Frontend
- 6. Web App to Function App: API call with the bearer token
- 7. Function App to Managed identity: Request a token
- 8. Managed identity to Private endpoint: Key Vault (identity line): Read secrets, with RBAC
- 9. Private endpoint: Key Vault to Key Vault
- 10. Managed identity to PostgreSQL Flexible Server: Connect with an Entra token
- Entra ID to App registrations: Registers the apps
- Entra ID to Privileged Identity Management: Elevates admins
- Azure Policy to Defender for Cloud: Policy results feed the score
- Function App to Azure Firewall: Egress through the hub
- Azure Firewall to Private DNS resolver: DNS proxy
- Private DNS resolver to private.postgres.database.azure.com
- Private DNS resolver to privatelink.vaultcore.azure.net
- Private DNS resolver to privatelink.azurewebsites.net
- Function App to Application Insights: Telemetry
- Application Insights to Log Analytics workspace

## Next steps

| Doc | Why |
|---|---|
| [Examples to copy](https://docs.plotwork.dev/examples.md) | Smaller diagrams, one idea each, with the prompt for each |
| [Cloud architecture](https://docs.plotwork.dev/cloud-architecture.md) | Accounts, regions, networks, subnets, and icons for AWS, Azure, and Google Cloud |
| [Writing diagrams](https://docs.plotwork.dev/writing-diagrams.md) | Numbered steps, animated lines, and the rest of the language |
| [Architecture patterns](https://docs.plotwork.dev/architecture-patterns.md) | Checked, ready-made diagrams to start from |

## See also

| Doc | Why |
|---|---|
| [Quick start](https://docs.plotwork.dev/quick-start.md) | Connect an agent and draw a first diagram |
| [Group types](https://docs.plotwork.dev/cloud-architecture/group-types.md) | The types of group used here, each drawn |
| [Icon names and lookup](https://docs.plotwork.dev/cloud-architecture/icon-names.md) | From a product name to an `icon=` |
