Zum Inhalt

Neuer spoke (Workload / Application Landing Zone)

Standardänderung: Neue Workload-/spoke-/Application-Landing-Zone (spoke canvas)

Auswirkung: Eine bestehende Azure-subscription wird zu einer einsatzbereiten landing zone. Sie wird in die gewählte Management Group verschoben, mit Tags versehen und umbenannt, an den hub angebunden und erhält die unten beschriebene Netzwerk-, Identity- und RBAC-Baseline.

Die Runbooks für Reviewer und Betreiber sind Spoke-Erstellung, Review des spoke-pull-requests und Spoke-Deployment.

Anfragefelder

Anfragefeld Wo es im spoke-JSON landet Hinweise
Name workloadShortName, plus azureSubscriptionName und der Dateiname workloadShortName hat höchstens 6 Zeichen. Es wird in jedem Ressourcennamen und im Terraform-state-Key verwendet. Wähle es sorgfältig: Eine spätere Änderung erzeugt einen neuen spoke.
Begründung pull request / Change-Record
Environment environment dev, tst/test, acc/acc<n>, prd/prod, shrd/shared. Die pipeline normalisiert den Wert nur, um spokes zu stages zu gruppieren (acc2 → acc, prod → prd). Namen und der state-Key verwenden den Rohwert, daher wären prod und prd zwei verschiedene spokes. Wähle eine Schreibweise und bleibe dabei.
Usage type (production data yes/no) managementGroupId und der Tag pii, falls personenbezogene Daten im Spiel sind Das JSON hat kein Feld für Produktionsdaten. Drücke es über die Wahl einer Management Group mit den passenden Policies aus, zum Beispiel eine untergeordnete Gruppe …-prdlike statt …-nonprd, falls deine Struktur sie hat.
Exposure to public internet yes/no managementGroupId (Baum landingzones_corp vs. landingzones_online) und InternetSecurityEnabled Eingehendes Publishing läuft über den hub Application Gateway (Runbook). Ausgehender Internetzugriff braucht Firewallregeln (Runbook).
IP range / IP size resourceconfiguration.network[].addressPrefix Vergib den Bereich aus deinem IP-Plan. Der Code prüft nicht auf Überschneidungen. Ein /24 ist ein üblicher Standard. Wenn du mehr brauchst, stimme das mit dem Eigentümer des IP-Plans ab. Trage den vergebenen Bereich im selben pull request im Plan ein.
Subnet name / size network[].subnets[].name / .addressPrefix
Subnet delegations network[].subnets[].delegations[] name muss einer der im Schema aufgelisteten Werte sein (zum Beispiel Microsoft.Web/serverFarms).
Subnet service endpoints network[].subnets[].serviceEndpoints[] { "service": "Microsoft.Storage" }
Which team needs access adInfo.ProductTeamGroupNames, adInfo.PIMGroupConfig Anzeigenamen bestehender Entra-ID-Gruppen.
Kommentare –

Die Anfrage prüfen

Wenn eine Anfrage eingeht, prüfe zuerst, ob die Angaben vollständig und korrekt sind. Schicke unklare Anfragen zurück, bis sie klar sind.

Punkt Prüfung
Workload-Kurzname Eindeutig über alle spokes. Höchstens 6 Zeichen. Nur Buchstaben und Ziffern: keine Leerzeichen oder Sonderzeichen.
Environment Eine der unterstützten Lebenszyklusphasen (Entwicklung, Test, Abnahme, Produktion). Verwende die Kürzel dev, tst, acc, prd einheitlich.
Tags Die erforderlichen Governance-Tags sind vorhanden: owner, department, costcentre, pii (ein Textwert, siehe die Referenz), dazu workload und environment. Die spoke-canvas-Anleitung verlangt Kleinbuchstaben; prüfe, was deine Tag-Policy verlangt. Füge nicht den reservierten Tag hidden-XebiaSpokeMetadata hinzu: Das Deployment fügt ihn hinzu.
Team-Gruppe Der Name der Product-Team-Gruppe, genau so wie in Entra ID.
Zielregionen Regionen, die es in deiner Plattform gibt. Verwende die Kurznamen deiner regioninfo (Gemeinsame Spoke-Einstellungen). Beispiele für Kurznamen: weu (West Europe), neu (North Europe).
Adressraum Ein /24 (256 Adressen) ist das übliche Minimum. Alles, was größer als ein /23 ist, braucht ein Gespräch und eine Begründung. Siehe auch IP-Bereich und Subnets planen.
Subnets Name, Größe, Delegierungen und Service Endpoints, gemäß Spoke-JSON-Referenz.
Management group Aus den Gruppen deiner Struktur gewählt, siehe unten.

Die management group wählen

Der spoke kommt in eine landing-zone-management-group. Zwei Entscheidungen bestimmen, in welche: ob der Workload aus dem Internet erreichbar ist (Corp oder Online) und ob er produktionsnahe Daten verarbeitet (PrdLike oder NonPrd). Mit einer canary- und einer Produktions-Plattformumgebung hat jede ihren eigenen Baum, also gibt es insgesamt acht Kombinationen:

Plattformumgebung Corp oder Online NonPrd oder PrdLike Management group (Beispielbenennung)
Canary Corp NonPrd <canary>CorpNonPrd
Canary Corp PrdLike <canary>CorpPrdLike
Canary Online NonPrd <canary>OnlineNonPrd
Canary Online PrdLike <canary>OnlinePrdLike
Produktion Corp NonPrd <platform>CorpNonPrd
Produktion Corp PrdLike <platform>CorpPrdLike
Produktion Online NonPrd <platform>OnlineNonPrd
Produktion Online PrdLike <platform>OnlinePrdLike

Die Namen sind ein Beispiel einer Implementierung. Verwende die IDs, die in deiner management_group_structure existieren (Hub-Config).

Bevor du loslegst

Zum ersten spoke einer neuen Plattform siehe Den spoke canvas deployen.

  • Die subscription muss bereits existieren. Der subscription-Layer kann eine subscription erstellen, aber nur, wenn azureSubscriptionId leer ist und nur mit einem CSP-Billing-Account (xmscsp_billing_account_name). Die pipeline übergibt die CSP-Werte als leere Strings (Invoke-XebiaMcpSpokePlan.ps1), also musst du über die pipelines eine bestehende subscription-ID angeben. Erstelle die subscription zuerst über deinen EA/MCA/CSP-Prozess.
  • Die spoke deployment identity (Azure.TerraformState.Spoke.ServicePrincipalId in mcp.json, verwendet von der spoke service connection) muss alles können, was unter Was erstellt wird aufgeführt ist:
  • die subscription zwischen Management Groups verschieben;
  • Rollenzuweisungen auf der subscription erstellen;
  • eine virtual hub connection in der connectivity-subscription erstellen;
  • Entra-ID-Gruppen in der Administrative Unit SpokeCommon.tenantinfo.AdministrativeUnitId erstellen;
  • service principals erstellen;
  • PIM für Gruppen konfigurieren.
  • Die Ziel-managementGroupId muss als id in <env-folder>/<targetRegions.primary of the spoke>/foundation.auto.tfvars vorkommen, sonst lehnt der Config-Test die Datei ab. Der Test akzeptiert jede id in management_group_structure und in den corp/online-Child-Maps. Wähle selbst eine landing-zone-Gruppe; der Test hindert dich nicht daran, zum Beispiel die Sandbox-Gruppe zu wählen.
  • Die Gruppen in ProductTeamGroupNames müssen in Entra ID existieren.
  • Die Genehmigungen, die deine Organisation verlangt, liegen vor, zum Beispiel vom Product Owner sowie von Architektur und Security für einen neuen Workload, und eine Registrierung des Workloads in deinem Asset Register (CMDB), falls du eines führst.
  • Der Adressbereich ist im IP-Plan reserviert (siehe Tabelle oben), und der Subnet-Plan lässt Platz für neue Subnets. Wenn du stattdessen einen IPAM-Pool nutzt, ist der Pool in den gemeinsamen Einstellungen aufgeführt (unten).
  • Wenn der spoke ein Azure-DevOps-Repository oder ein GitHub-Repository erstellt, ist die Vorbereitung dafür erledigt (Spoke-Integrationen vorbereiten).

Schritte

1. Die spoke-Datei erstellen

Erstelle zuerst einen Branch. Ein einheitlicher Branch-Name macht Branches leicht auffindbar, zum Beispiel feature/SpokeNew-<request id>-<workload short name>-<environment>. Prüfe deine eigenen Konventionen.

Erstelle dann <env-folder>/spokes/<team-or-workload>/<file>.json. Am einfachsten startest du mit einer Kopie der Template-spoke-Datei, die mit dem Konfigurations-Repository oder der gepinnten Code-Version kommt. Template-Dateien für häufige Muster können im selben Repository liegen, aber gib ihnen eine andere Endung als .json oder lege sie dorthin, wo der glob nicht greift, damit die pipeline sie nicht deployt.

  • Verwende einen Unterordner. Der Standard-glob **/*.json braucht ein /, daher werden Dateien direkt in spokes/ nicht erfasst.
  • Verwende einen eindeutigen Dateinamen über alle Unterordner hinweg. Der Dateiname ohne .json wird zum Namen des spokes in pipeline-Jobs und Artefakten (spoke_<name>, subscription_output_<name>). Zwei Dateien mit demselben Namen in verschiedenen Ordnern kollidieren in einem Lauf.
  • Ein einheitliches Muster wie canvas_<workload>_<environment>.json macht spokeGlob einfach nutzbar.
  • Die separate spoke-canvas-pipeline erwartet mehr: Der Name beginnt mit canvas_, endet auf .json und enthält _prod_ oder _nonprod_ (zum Beispiel canvas_nonprod_<workload>-<environment>.json). Halte dich an dieses Muster, wenn du diese pipeline betreibst.

Minimales Beispiel (gültig gegen src/Xebia.Mcp/Files/json/spokeschema.json):

{
  "workloadShortName": "app1",
  "environment": "dev",
  "azureSubscriptionId": "00000000-0000-0000-0000-000000000000",
  "azureSubscriptionName": "sub-app1-dev",
  "managementGroupId": "cloudplatform-landingzones-corp",
  "tags": {
    "owner": "Jane Doe",
    "department": "Finance IT",
    "workload": "Invoice Portal",
    "pii": "no",
    "costcentre": "CC-1234",
    "environment": "dev"
  },
  "adInfo": {
    "ProductTeamGroupNames": ["Team Invoice Portal"],
    "PIMGroupConfig": {
      "Readers": "nopim",
      "Contributors": "self-approve",
      "Owners": "approval"
    }
  },
  "targetRegions": {
    "primary": "weu",
    "additional": []
  },
  "resourceconfiguration": {
    "network": [
      {
        "name": "vnet-##wlshortname##-##environment##-##region##-001",
        "region": "weu",
        "addressPrefix": ["10.20.4.0/24"],
        "peerHub": true,
        "dnsServerOverwrite": "",
        "subnets": [
          {
            "name": "snet-##wlshortname##-pe-##environment##-##region##-001",
            "isprivatelinksubnet": true,
            "addressPrefix": "10.20.4.0/27",
            "privateEndpointNetworkPoliciesEnabled": false,
            "serviceEndpoints": [],
            "delegations": [],
            "linkedroutetablename": null,
            "linkednetworksecuritygroupname": "nsg-##wlshortname##-##environment##-##region##-001"
          },
          {
            "name": "snet-##wlshortname##-app-##environment##-##region##-001",
            "isprivatelinksubnet": false,
            "addressPrefix": "10.20.4.64/26",
            "privateEndpointNetworkPoliciesEnabled": false,
            "serviceEndpoints": [{ "service": "Microsoft.Storage" }],
            "delegations": [
              {
                "name": "Microsoft.Web/serverFarms",
                "actions": ["Microsoft.Network/virtualNetworks/subnets/action"]
              }
            ],
            "linkedroutetablename": null,
            "linkednetworksecuritygroupname": "nsg-##wlshortname##-##environment##-##region##-001"
          }
        ],
        "networksecuritygroups": [
          { "name": "nsg-##wlshortname##-##environment##-##region##-001", "rules": [] }
        ],
        "routetables": []
      }
    ]
  }
}

Die Platzhalter ##wlshortname##, ##environment## und ##region## werden in den Namen von Netzwerk, Subnets, NSGs und Route Tables ersetzt.

Zwei strukturelle Regeln, die das Schema nicht erzwingt, auf die sich Terraform aber verlässt:

  • Jeder network[]-Eintrag braucht mindestens ein Subnet mit "isprivatelinksubnet": true (modules/virtual_network/locals.tf nimmt das erste);
  • wenn der Key Vault oder das storage account deployt wird (Standard), braucht jede Region in targetRegions ein VNet mit einem solchen Subnet. Dort werden die private endpoints platziert. Alle Felder, einschließlich der optionalen Blöcke keyvault, storageaccount, loganalytics, azdo und extensions, sind in der spoke-JSON-Referenz beschrieben.

Adressraum aus einem IPAM-Pool

Wenn die Plattform IPAM-Pools hat (Gemeinsame Spoke-Einstellungen), kannst du addressPrefix weglassen und stattdessen den Pool-Namen und die Größen angeben. Der Pool wählt die Adressen, und das Überschneidungsrisiko handgeschriebener Bereiche entfällt. Die Felder und Regeln stehen in der Spoke-JSON-Referenz. Trage die zugewiesenen Bereiche danach aus dem Deployment-Output in deinen IP-Plan ein.

2. Über die optionalen Ressourcen entscheiden

Wenn du die Blöcke storageaccount, keyvault und loganalytics weglässt, werden trotzdem ein storage account, ein Key Vault und ein Log Analytics workspace mit Modul-Defaults erstellt (deploy ist standardmäßig true in spoke-canvas/deploy/spoke/locals.tf und main.tf). Ein storage account erzwingt immer einen Key Vault, der seinen customer-managed key enthält. Um sie zu überspringen, füge den Block mit "deploy": false hinzu und mit allen Schlüsseln, die das Schema für diesen Block verlangt (siehe die Referenz).

Erstelle den private endpoint des Key Vaults oder des storage accounts (createprivateendpoint, CreatePrivateEndpoint) nur dann, wenn der Workload ihn nutzen wird. Der Key Vault enthält das Secret des Deployment-Service-Principals, sofern der spoke keine federated credentials verwendet. Bevorzuge federated credentials (azdo.useFederatedCredentials, wenn du die Azure-DevOps-Service- Connection oder die GitHub-Extension erstellst), damit kein Secret gespeichert oder rotiert werden muss. Beide Integrationen sind in Spoke-Integrationen beschrieben.

3. Validieren und einen pull request öffnen

Prüfe die Datei noch einmal, committe sie in den Branch und öffne einen pull request. Eine andere Person prüft ihn (Review des spoke-pull-requests). Folge Änderungen sicher durchführen, Schritte 4–5.

4. Die spoke-pipeline ausführen

Führe spoke aus, wobei spokeGlob nur auf die neue Datei zeigt, zum Beispiel team-a/canvas_app1_dev.json.

  • Azure DevOps: Pipelines → spoke → Run pipeline → spokeGlob = team-a/canvas_app1_dev.json.
  • GitHub Actions: Actions → spoke → Run workflow → spokeGlob = team-a/canvas_app1_dev.json.

Führe nicht mit dem Standard **/*.json aus, es sei denn, du willst jeden spoke planen. Jede vom glob erfasste spoke-Datei muss die Config-Tests bestehen, und ihr environment muss in der environments-Liste der pipeline stehen (Standard dev → tst → acc → prd). Ein shared-spoke lässt den Lauf fehlschlagen, es sei denn, du fügst shared zu environments hinzu.

Stelle sicher, dass die Entry-pipeline nicht whatIf: true hat, wenn du den spoke anwenden willst (siehe Pipelines und Workflows).

Was erstellt wird

Aus src/Xebia.Mcp/Files/terraform/spoke-canvas/deploy/spoke/main.tf:

Ressource Name / Detail
Subscription-Tags Die tags aus dem JSON plus spoke_canvas_version
Management-Group-Platzierung Verschiebt die subscription nach managementGroupId, falls sie dort noch nicht ist
Subscription-Umbenennung In azureSubscriptionName. Wenn leer, in SpokeCommon.tenantinfo.SubsciptionNamingTemplate
Resource group (pro Region) rg-canvas-<wl>-<env>-<region>-001, es sei denn, resourceGroup.name ist gesetzt
Virtual network, Subnets, NSGs, Regeln, Route Tables Aus network[]. Jedes Subnet erhält einen NSG: den aus linkednetworksecuritygroupname oder einen Default-NSG
DNS-Server am virtual network dnsServerOverwrite oder der hub-DNS-Server der Region (virtual_network_dns_servers in den hub-connectivity-tfvars)
Virtual hub connection (wenn peerHub, Standard true) VNet-Name mit vnet- → con-, in der connectivity-subscription, internet_security_enabled = InternetSecurityEnabled (Standard true)
Log Analytics workspace (standardmäßig an) log-<wl>-<env>-<region>-001
Key Vault (standardmäßig an) kv<wl><env><region>001, RBAC-Modus, standardmäßig private endpoint
Storage account (standardmäßig an) st<wl><env><region>001, customer-managed key, standardmäßig private endpoint
Entra-ID-Gruppen <GroupnamePrefix><wl>_<env>_Owners, _Contributors, _Readers, _Support in der Administrative Unit
PIM Jede ProductTeamGroupNames-Gruppe wird Mitglied jeder spoke-Gruppe: nopim = aktiv, self-approve = eligible ohne Genehmigung, approval = eligible mit Genehmigung
Service principal <SPNNamePrefix><wl>-<env>. Sein Secret wird im spoke-Key-Vault gespeichert, es sei denn, es werden federated credentials verwendet
Rollenzuweisungen Subscription: SPN und Owners-Gruppe → Owner, Contributors → Contributor, Readers → Reader. Dazu Data-Plane-Rollen auf Key Vault und storage account, vergeben über die role map auf subscription-Ebene (RBAC und PIM)
Azure-DevOps-Repo und service connection Nur wenn der Block azdo vorhanden ist
GitHub-Repository und Federated Credentials Nur wenn extensions.github aktiviert ist (Spoke-Integrationen)

Nicht vom spoke canvas erstellt: Budgets und Firewallregeln für den Traffic des spokes.

Prüfen

  1. Der Apply-Schritt der pipeline für die spoke-Umgebung ist grün.
  2. Im Azure portal gilt für die subscription:
  3. sie liegt unter managementGroupId;
  4. sie hat die Tags;
  5. sie hat das virtual network mit dem erwarteten Adressraum und den erwarteten Subnets.
  6. Connectivity-subscription → Virtual WAN → Virtual network connections: Das spoke-VNet ist als verbunden aufgelistet.
  7. Entra ID: Die vier SG_SPOKE_<wl>_<env>_*-Gruppen existieren, und die Team-Gruppe ist wie konfiguriert (eligible) Mitglied.
  8. Bitte das Team, seine Rolle zu aktivieren (falls PIM gilt), und den Zugriff zu bestätigen.

Rollback

Entferne den spoke mit dem Runbook Delete spoke. Das Löschen der JSON-Datei allein bewirkt nichts: Keine pipeline zerstört spokes, deren Datei fehlt.

Wenn der spoke Managed VMs bekommt, füge seinen Ordner zur Liste der spokes der Modul-pipeline hinzu (siehe Managed-VM-Modul).

Häufige Probleme

Symptom Ursache
Config-Test: environment … must be one of Das environment des spokes steht nicht in der environments-Liste der pipeline.
Config-Test: managementGroupId … must exist Die Gruppe steht nicht in der foundation.auto.tfvars der primären Region.
Config-Test: Schemafehler Fehlender Pflicht-Key. Jedes Subnet braucht isprivatelinksubnet, privateEndpointNetworkPoliciesEnabled, serviceEndpoints, delegations und linkedroutetablename.
Der Plan will für einen bestehenden spoke einen komplett neuen spoke erstellen workloadShortName oder environment wurde geändert, was den state-Key ändert.
Ein erneuter Lauf plant, azurerm_management_group_subscription_association zu zerstören Bekanntes Problem: Das count der Association hängt davon ab, ob die subscription bereits in der Gruppe ist. Siehe Bekannte Probleme. Wende dieses destroy nicht an, ohne es zu verstehen.
Der spoke erreicht das Internet oder andere Netzwerke nicht Firewallregeln fehlen. Siehe Firewallregeln.