A global express, freight, and supply-chain carrier that moves parcels, time-critical freight, and temperature-controlled healthcare shipments through a worldwide air-and-ground network of sorting hubs, distribution centres, and a last-mile delivery fleet asked a deceptively simple question: “Give us one secure foundation we can build the next decade on.” Behind that question sits a brutal amount of reality — four on-premises data centres in four countries, a dual-cloud mandate across Azure and AWS, more than 100 applications, SAP at the centre of the business, a hybrid workforce signing in from offices and from home, and a fleet of trucks and containers that needs to report its location and condition in near real time. This article is the target-state reference architecture for that foundation: a secure, policy-driven, multi-cloud landing zone built on the Cloud Adoption Framework, enterprise-scale landing zones, and Well-Architected principles, and justified — not just asserted — with twenty-seven architecture diagrams.
The design brief is uncompromising. Identity is the perimeter, not the network. Platform services come before applications. Platform subscriptions and accounts stay separate from workload subscriptions and accounts so that a compromise or a mistake has a small blast radius. Guardrails, naming, tagging, logging, and CI/CD behave identically whether a team lands in Azure or AWS. And every critical service is engineered for regional and circuit redundancy rather than convenience. The result is not a flat trust zone stretched into the cloud; it is the same or better user and operator experience delivered through integrated identity, DNS, routing, and policy while zero-trust enforcement runs underneath every request.
Target-state overview
The estate is best understood from a single vantage point before drilling into any one domain. Users and partners enter from the top — office staff, work-from-home staff over GlobalProtect VPN, and external partners. Every public request first meets a third-party global edge (CDN, authoritative DNS with global traffic management, and a WAF) that normalises and protects traffic before it touches a cloud. Azure and AWS each host enterprise landing zones; the four on-premises data centres anchor the bottom and connect to both clouds over dual private circuits. Cross-cutting platforms — Okta, Microsoft Entra ID, Wiz, CrowdStrike Falcon, Dynatrace, and ServiceNow — operate horizontally across both clouds so security, observability, and operations are consistent. A dedicated IoT lane carries telemetry from trucks and containers into the platform.
Two design decisions in this overview drive everything that follows. First, the edge is a third-party global tier, not a per-cloud feature. A single CDN/DNS/WAF stack in front of both clouds gives one place to enforce OWASP rules, bot mitigation, API protection, and geo policy, and it lets traffic fail over between Azure and AWS origins without re-architecting each application. Second, the four on-premises data centres are first-class regions, not legacy to be tolerated. SAP dependencies, AD DS, Kerberos-bound systems, and country-level business continuity all assume the on-premises footprint persists, so the connectivity and identity designs treat hybrid as the default rather than a migration afterthought.
Architecture principles and operating model
Six principles govern the build. Platform first — identity, networking, management, and security are established before any workload migrates. Segregation by design — platform and application boundaries never share a subscription or account. Zero trust — no user, device, workload, or path is trusted implicitly; access is evaluated continuously on identity, device, risk, and context. Hybrid by default — every critical service accounts for on-premises integration, SAP, and AD DS. Multi-cloud consistency — one set of guardrails, naming, logging, and CI/CD across both clouds. Resilience over convenience — regional redundancy, redundant circuits, and tested recovery beat the easy path.
These principles only hold if someone owns them. The operating model centres on a Cloud Center of Excellence that owns landing-zone standards, policy-as-code, reference architectures, and onboarding patterns. Around it sit five teams with clear remits: Cloud Platform Engineering (landing zones, automation, shared tooling), Cloud Security and Identity (Okta/Entra federation, PIM, Conditional Access, Wiz, CrowdStrike, secrets, governance), Network and Connectivity (ExpressRoute, Direct Connect, transit, firewalls, DNS, edge, GlobalProtect), Cloud Operations (monitoring, backup, incident response, ServiceNow), and Application Enablement (onboarding blueprints by workload type). The consulting partner’s job is to make those teams self-sufficient after handover, which is why every pattern in this architecture is expressed as reusable code and documented runbooks rather than tribal knowledge.
Requirements and non-functional targets
A design is only as good as the targets it can be tested against, so before any topology is drawn the proposal pins down what the estate must do and how well it must do it. The functional scope is broad but deliberately bounded: a dual-cloud landing zone across Azure and AWS, a zero-trust identity fabric spanning on-premises Active Directory, Entra ID, and Okta, resilient hybrid connectivity from the four data centres, a factory to onboard 100-plus applications, a self-managed SAP landing zone, an event-driven data and integration platform, a fleet-scale IoT backbone, a location-transparent digital workplace, an end-to-end DevSecOps pipeline, and a unified observability and SOC capability. Each of those is a deliverable in its own right; what turns them from a wish list into an engineerable system is the set of non-functional targets that follow, because those are the numbers the platform will be accepted — or rejected — against.
The headline non-functional is availability, and it is engineered to the recovery tier rather than promised as a single blanket figure. The platform and Tier-0 control plane — identity, connectivity, DNS, and security tooling — carries a monthly SLO of 99.99%, because nothing else recovers until it does. Tier-1 services that the business sells against — customer logistics platforms, SAP production, and partner APIs — target 99.95%; Tier-2 line-of-business and analytics workloads target 99.9%; and Tier-3 development, test, and non-critical reporting relax to 99.5%. Latency is treated with the same specificity: edge-cached time-to-first-byte stays under 100 ms globally, the control-tower and tracking APIs hold a p95 under 300 ms in-region, and the real-time IoT alert path — geofence and threshold breaches that a dispatcher acts on — completes end-to-end in under 5 seconds. Scale targets are sized to the fleet the carrier actually runs: 500,000-plus connected devices, 50,000 messages per second at peak ingest, 10,000 tracking events per second sustained, and headroom to onboard 100-plus applications without re-architecting the platform.
Security and operational agility close out the envelope, and both are expressed as measurable commitments rather than intentions, because a posture that cannot be measured cannot be defended in an audit. Every user authenticates with MFA (100% coverage), privileged roles use phishing-resistant factors, and the estate runs with zero standing privilege — all administrative access is brokered just-in-time through PIM with approval and a time box. Posture is held to a Wiz score of at least 85 across both clouds, and the patch SLA is seven days for critical and thirty days for high findings, tracked to closure rather than to detection. On agility, a new subscription or account is vended in under one business day and a new application reaches production-ready in under two weeks through the blueprint — the deliberate counterweight to the governance, so that control does not become a synonym for slow. Recovery objectives are governed by the DR-tier table reproduced in the resiliency section, and the compliance posture — ISO 27001, SOC 2 Type II, PCI-DSS on payment paths, and GDPR with country data-residency — is detailed in the compliance section, with this document carrying only the obligation that the targets exist and are owned. The table below consolidates the non-functionals into the form the programme tracks them in: a dimension, a hard target, and the mechanism by which it is met and measured.
| Dimension | Target | How it’s met / measured |
|---|---|---|
| Platform / Tier-0 availability | 99.99% monthly | Active-active control plane, paired-DR region per geography, RTO 1h / RPO 15m |
| Tier-1 availability | 99.95% monthly | Customer platforms, SAP prod, partner APIs across 2 AZs + warm DR |
| Tier-2 / Tier-3 availability | 99.9% / 99.5% monthly | Single-region resilient with backup-driven recovery to tier RTO/RPO |
| Edge latency | TTFB < 100 ms globally | Third-party CDN anycast caching + health-checked DNS failover |
| API latency | Control-tower & tracking p95 < 300 ms in-region | Regional active-active, private back ends, Dynatrace RUM/synthetic |
| IoT alert latency | End-to-end < 5 s | Rules engine + stream processing on the hot path, geofence/threshold |
| Device & ingest scale | 500,000+ devices, 50,000 msg/sec peak, 10,000 events/sec sustained | AWS IoT Core + Azure IoT Hub, sharded streaming, load-tested |
| Security posture | MFA 100%, zero standing privilege, Wiz ≥ 85 | Conditional Access + PIM JIT, continuous CSPM scoring |
| Patch SLA | Critical 7 days / High 30 days | Monthly cadence + critical out-of-band, tracked in ServiceNow |
| Operational agility | Vend < 1 business day, new app < 2 weeks | Subscription/account factories + application blueprint |
Requirements traceability
Stating targets is necessary but not sufficient; the proof that the architecture answers them is traceability — every requirement mapped to a concrete design element and to the diagram that evidences it, so that nothing is asserted in prose and then quietly orphaned. The discipline matters most on a dual-cloud estate, where it is easy for a control to be real in Azure and merely assumed in AWS, or for a resilience claim to lack a rehearsal behind it. The register below is the spine that connects the two halves of the document: read left to right, each row begins with a business or platform requirement, moves to the design response that satisfies it, and ends with the section or diagram a reviewer can open to confirm the response is actually built rather than promised.
The mapping is deliberately complete across the estate’s load-bearing concerns, and it is bidirectional in spirit: every requirement has a design response, and — just as important — no major design element exists without a requirement that justifies it, which is how gold-plating is kept out of the architecture. Guardrail consistency is satisfied by the subscription and account factories that vend governed landing zones identically in both clouds; zero-trust identity by the Conditional Access and PIM model over Entra and Okta; resilient connectivity by the dual ExpressRoute and Direct Connect design; and scale, SAP resilience, multi-layer security, IoT throughput, observability, data residency, and operability each trace to their own design response and evidence. Where a requirement spans several diagrams — multi-layer security and application onboarding both do — the traceability points at the set, because the control only holds when every layer is present. The final row is the one that is easiest to omit and most expensive to skip: the estate must remain operable after handover, which traces to the RACI and the runbooks in the operating-model section, because a platform nobody can run to its targets has not actually met them.
| Req ID | Requirement | Design response | Evidence (section / diagram) |
|---|---|---|---|
| REQ-01 | Consistent dual-cloud guardrails | Subscription factory + Account Factory for Terraform, one policy-as-code set | Azure & AWS landing zone sections / governance hierarchy diagram |
| REQ-02 | Zero-trust identity, no standing privilege | Entra + Okta, Conditional Access engine, PIM JIT with approval and time-box | Identity & Conditional Access sections / identity & CA-PIM diagrams |
| REQ-03 | Resilient hybrid connectivity from 4 DCs | 2× ExpressRoute + 2× Direct Connect per region, active-active BGP | Global hybrid connectivity section / connectivity diagram |
| REQ-04 | Onboard 100+ applications | Application blueprints + factory vending, secure-by-default landing zones | Application onboarding section / onboarding diagrams |
| REQ-05 | SAP high availability and DR | HANA System Replication across 2 AZs + asynchronous warm-standby DR | SAP landing zone section / SAP diagram |
| REQ-06 | Multi-layer, defence-in-depth security | Seven-layer model + layered edge ingress, centralised inspection | Multi-layer security sections / security & network diagrams |
| REQ-07 | Fleet-scale IoT ingest | Split AWS IoT Core (vehicle telemetry) + Azure IoT Hub (control tower) | IoT connected logistics section / IoT diagrams |
| REQ-08 | Unified observability and SOC | Dynatrace strategic plane, Sentinel SIEM, automated ServiceNow incidents | Observability & SOC section / SOC diagram |
| REQ-09 | Data residency and localisation | Medallion lakehouse governance band enforcing in-region restricted data | Data & integration section / data platform diagram |
| REQ-10 | Operable to target after handover | 6-team RACI, tiered DR runbooks, follow-the-sun support model | Operating model & DR runbooks section / governance material |
Azure landing zone
Azure follows the enterprise-scale design. A Tenant Root Group anchors a management-group hierarchy that pushes policy and RBAC down by inheritance, so a control written once applies everywhere beneath it. The Platform management group holds four dedicated platform subscriptions — Identity, Connectivity, Management, and Security — which keeps shared services out of any single workload’s blast radius and lets the platform teams operate them on their own change cadence. Application workloads live under a Landing Zones management group, split into Corp (internal) and Online (internet-facing) and further separated into production and non-production subscriptions. Sandbox and Decommissioned management groups give experimentation and offboarding their own guarded lifecycles.
The platform subscriptions earn their separation. Identity carries domain controllers, Entra Connect, and private DNS support. Connectivity holds the regional hub VNets, Azure Firewall Premium, Application Gateway WAF, the ExpressRoute gateway, DNS Private Resolver, Bastion, and DDoS Network Protection. Management centralises Log Analytics, Azure Monitor, Automation, Update Management, and Backup vaults. Security runs Microsoft Sentinel, Key Vault HSM, break-glass identities, and the integration anchors for Wiz and CrowdStrike. Application subscriptions are vended, not hand-built: a subscription factory places each new subscription under the right management group, inherits policy, assigns RBAC, applies cost tags, and attaches it to the hub — so a workload arrives already governed.
AWS landing zone
AWS mirrors the same intent through AWS Organizations and a multi-account model delivered with Control Tower and Account Factory for Terraform. The Management account sits at the apex; Service Control Policies enforce the same guardrails the Azure policy hierarchy enforces — approved regions only, no public exposure of sensitive services, mandatory tagging. Security-relevant accounts are isolated in a Security OU (a Log Archive account with immutable S3 and Object Lock, and a Security Tooling account). Infrastructure lives in its own OU (a Shared Network account per region carrying the Transit Gateway, and a Shared Services account). Workloads split into Prod and Non-Prod OUs, with application accounts separated by criticality.
The decisive pattern here is account vending as code. Account Factory for Terraform provisions and customises accounts through a GitOps workflow, so an application account is never a manual ticket — it is a pull request that yields an account already wired with centralised logging, security-tooling enrolment, a Transit Gateway attachment, IAM Identity Center federation, approved-region settings, and KMS governance. That symmetry between Azure subscription vending and AWS account vending is what makes “multi-cloud consistency” real rather than aspirational: a workload team requests an environment the same way and receives the same baseline regardless of cloud.
Governance hierarchy and policy inheritance
The two landing zones look different on the surface — Azure management groups on one side, AWS organisational units on the other — but they are governed by one set of guardrails compiled into both. Policy-as-code in Terraform defines each control once; it is rendered into Azure Policy and RBAC assignments that inherit down the management-group tree, and into Service Control Policies that inherit down the AWS OU tree. A control authored once — approved regions only, no public exposure of sensitive services, mandatory tagging, encryption everywhere, logging to an immutable archive — therefore applies identically in both clouds, which is exactly what stops a multi-cloud estate from drifting into two divergent ones.
Inheritance is the mechanism that lets the operating model scale: the platform teams change a guardrail in one place and every subscription and account beneath it conforms on the next pipeline run, while workload teams stay free to build inside the guardrails without being able to weaken them.
Global hybrid connectivity
Connectivity is where the four-data-centre reality becomes architecture. Four strategic regions align to the four on-premises countries. Each region terminates two ExpressRoute circuits to Azure and two Direct Connect circuits to AWS, ideally across diverse providers, with active-active BGP so a single circuit or carrier failure fails over without human intervention. Azure regional hub VNets front the ExpressRoute gateways and route through Azure Firewall to their spokes; AWS regional Transit Gateways attach behind Direct Connect Gateways and fan out to VPCs. Controlled inter-cloud traffic between Azure and AWS routes either through the on-premises core or a dedicated interconnect, chosen per workload on latency, compliance, and cost.
Redundancy here is not gold-plating; it is the explicit requirement. A logistics control tower that loses connectivity to SAP or to its tracking back end stops the business, so every region carries two of everything and the routing is engineered to failback cleanly once a circuit recovers. The hub-and-spoke pattern in Azure and the transit-centric pattern in AWS both centralise inspection and egress control, which means the network security policy is enforced in one place per region instead of being re-implemented per workload.
That WAN view shows where the circuits land; the regional internals differ enough per cloud to deserve their own deep dives. In Azure, each region is a hub-and-spoke VNet: the hub carries the ExpressRoute and backup VPN gateways, Azure Firewall Premium, Application Gateway WAF, Bastion, and DNS Private Resolver in their own dedicated subnets, and spoke VNets for prod and shared services peer in — forced through the firewall by user-defined routes and reaching PaaS only over Private Endpoints.
AWS uses a transit-centric design: a regional Transit Gateway with separate route tables stitches together a dedicated inspection VPC (AWS Network Firewall in appliance mode), an ingress VPC (ALB + WAF across two Availability Zones), an egress VPC (a NAT gateway per AZ), and the workload VPCs — each reaching AWS services privately through Gateway and Interface VPC endpoints, with the Direct Connect Gateway terminating the dual circuits.
IP address management plan
Addressing on a multi-cloud estate is a governance problem long before it is a routing problem. The CIDR ranges that appear throughout the network diagrams are not chosen per project; they are issued from a single, centrally governed plan held in Azure IPAM and AWS VPC IPAM, so that every block is globally non-overlapping and an address can be read like a coordinate. The whole private estate lives inside the 10.0.0.0/8 supernet, allocated as a predictable /16 per (cloud, region, environment, role) — large enough that no workload ever has to renumber, and structured enough that a packet’s source address tells an operator which cloud, which region, and which tier it came from. That discipline is what makes the firewall rules, the route tables, and the on-premises BGP advertisements tractable across two clouds and four data centres.
The Azure plan encodes meaning into the second and third octets with the scheme 10.[role][region].0.0/16: the role digit is 1 for hub/platform, 2 for production, 3 for shared/non-prod, and the region digit runs 0–3 for the four primary regions and 4–7 for their paired-DR partners. So Region-1’s primary hub is 10.10.0.0/16, its production spoke is 10.20.0.0/16, and its shared/non-prod spoke is 10.30.0.0/16 — exactly the blocks the regional network diagrams carry — while the same region’s DR partner mirrors them at 10.14/10.24/10.34. Inside the Region-1 hub the subnets are themselves reserved by purpose (GatewaySubnet, AzureFirewallSubnet, AppGatewaySubnet, AzureBastionSubnet, and the DNS Private Resolver), and the production spoke is tiered into web, app, and data/Private-Endpoint subnets. AWS follows the same philosophy with its own ranges: a network/inspection VPC at 10.100.0.0/16, ingress and egress VPCs alongside it, a production application VPC at 10.200.0.0/16, and a shared-services VPC at 10.210.0.0/16, with two Availability Zones and /20 subnets per AZ per tier so capacity is never the constraint. On-premises occupies 172.16.0.0/12, partitioned cleanly into four /14 blocks — one per data centre — and 192.168.0.0/16 is held back entirely for lab, edge, and OT use so it can never collide with a routed range.
The master allocation below is the authoritative slice of that plan. Every row is a block the factories may draw from; everything outside it is, by policy, unallocated and therefore safe to grow into. The key invariant is that the address is the identity — because the role and region are baked into the prefix, segmentation rules and residency boundaries can be expressed against CIDR ranges directly, and a misrouted or unexpectedly sourced packet is visible the moment it crosses a firewall.
| Scope | Region / Env | CIDR | Purpose |
|---|---|---|---|
| Global supernet | All | 10.0.0.0/8 | Centrally governed private space (Azure IPAM + AWS VPC IPAM) |
| Azure hub | Region-1 primary | 10.10.0.0/16 | Connectivity hub — gateways, firewall, App Gateway, Bastion, DNS |
| Azure prod | Region-1 primary | 10.20.0.0/16 | Production workload spokes (web 10.20.1.0/24, app 10.20.2.0/24, data/PE 10.20.3.0/24) |
| Azure shared | Region-1 primary | 10.30.0.0/16 | Shared services / non-production spokes |
| Azure hub | Region-1 DR | 10.14.0.0/16 | Paired-DR connectivity hub (prod 10.24.0.0/16, shared 10.34.0.0/16) |
| Azure region-2 | Primary (hub/prod/shared) | 10.11.0.0/16 / 10.21.0.0/16 / 10.31.0.0/16 | Region-2 strategic geography |
| Azure region-3 | Primary (hub/prod/shared) | 10.12.0.0/16 / 10.22.0.0/16 / 10.32.0.0/16 | Region-3 strategic geography |
| Azure region-4 | Primary (hub/prod/shared) | 10.13.0.0/16 / 10.23.0.0/16 / 10.33.0.0/16 | Region-4 strategic geography |
| AWS network | Region-1 primary | 10.100.0.0/16 | Network / inspection VPC (Network Firewall, Transit Gateway) |
| AWS ingress/egress | Region-1 primary | 10.101.0.0/16 / 10.102.0.0/16 | Ingress VPC (ALB + WAF) and egress VPC (NAT per AZ) |
| AWS prod app | Region-1 primary | 10.200.0.0/16 | Production application VPC (two AZs, /20 subnets per tier) |
| AWS shared services | Region-1 primary | 10.210.0.0/16 | Shared-services VPC |
| On-premises | 4 data centres | 172.16.0.0/12 | DC1 172.16/14, DC2 172.20/14, DC3 172.24/14, DC4 172.28/14 |
| Reserved | Lab / edge / OT | 192.168.0.0/16 | Held back — never routed into the cloud estate |
Capacity and sizing
Capacity on this estate is engineered against the non-functional targets, not guessed at from a rule of thumb. Every tier is sized to the service-level objective and throughput it must sustain, and almost everything autoscales with a floor — a minimum instance count or zone-redundant footprint that holds availability up even at idle, scaling out under load rather than being provisioned for peak the whole month. The connectivity layer is the foundation: each region terminates two ExpressRoute circuits at 10 Gbps (Premium) and two Direct Connect circuits at 10 Gbps (dedicated) in active-active BGP, fronted by an Azure ErGw3AZ gateway with a VpnGw2AZ backup on the Azure side and a Direct Connect Gateway plus Transit Gateway on the AWS side. That is deliberate over-provisioning — a control tower that loses its path to SAP or to tracking stops the business, so the circuits carry headroom and fail over cleanly rather than running hot.
Above the network, compute is sized per cloud and per domain. The Azure edge and security plane runs Azure Firewall Premium (auto-scaling) and a zone-redundant Application Gateway WAF v2 that scales between 2 and 10 instances; web and application tiers run on VM Scale Sets and AKS node pools that scale from a resilient floor toward generous ceilings, backed by zone-redundant Azure SQL Business Critical and PostgreSQL Flexible Server. AWS mirrors the pattern with ALB and Network Firewall, m6i-class Auto Scaling groups and EKS node groups, and Aurora Multi-AZ or RDS for stateful data. SAP is the outlier that earns bespoke sizing: HANA runs on certified high-memory compute — Azure M-series (M128s, ~2 TB) or AWS High Memory / X2iedn — with clustered ASCS/ERS on D-series, HANA System Replication synchronous between the two production zones and asynchronous to the DR region. The IoT lane is sized to the hardest numbers in the brief — 500,000+ connected devices and a 50,000 msg/sec peak — across AWS IoT Core with throughput-sharded Kinesis into Timestream, and Azure IoT Hub with an Azure Data Explorer cluster, while the data backbone runs Event Hubs premium / Kinesis feeding a lakehouse on ADLS Gen2 and S3.
The table below maps each domain to the service and scale unit it is built on and the autoscale or high-availability posture that protects it. The discipline running through all of it is that resilience is a floor, not a feature toggled on under load — minimum instance counts, zone redundancy, and rehearsed replication are sized in from the start, and elasticity handles the variance above that line rather than the availability of the service itself.
| Domain | Service / SKU | Scale unit | Autoscale / HA |
|---|---|---|---|
| Hybrid connectivity | 2× ExpressRoute (Premium) + 2× Direct Connect (dedicated) per region | 10 Gbps per circuit | Active-active BGP; ErGw3AZ + VpnGw2AZ backup; DX Gateway + TGW |
| Azure edge security | Azure Firewall Premium | Per-region instance | Auto-scale, zone-redundant |
| Azure ingress | Application Gateway WAF v2 | 2–10 instances | Autoscale, zone-redundant |
| Azure web tier | VMSS Standard_D4s_v5 | 3–20 instances | Autoscale across zones |
| Azure app tier | VMSS Standard_D8s_v5 | 3–20 instances | Autoscale across zones |
| Azure containers | AKS node pool D8s_v5 | 3–30 nodes | Cluster autoscaler, zone-spread |
| Azure data | Azure SQL Business Critical / PostgreSQL Flexible | Per-database | Zone-redundant replicas |
| AWS workloads | m6i.xlarge / 2xlarge Auto Scaling; EKS m6i.2xlarge | Auto Scaling group / node group | Multi-AZ autoscale; ALB + Network Firewall |
| AWS data | Aurora (Multi-AZ) / RDS | Per-cluster | Multi-AZ replicas, automated failover |
| SAP HANA | Azure M128s (~2 TB) / AWS High Memory u-* / X2iedn | High-memory node per zone | HSR sync primary↔secondary AZ; async to DR |
| IoT ingestion | AWS IoT Core + Kinesis + Timestream; Azure IoT Hub + ADX | 500k devices, 50k msg/sec peak | Sharded to throughput; scaled units, ADX cluster |
| Data & integration | Event Hubs premium / Kinesis; lakehouse on ADLS Gen2 + S3 | Throughput units / shards | Elastic streaming; Databricks/Synapse + Glue/EMR |
Identity and zero-trust control plane
Identity is the primary security perimeter, and the design is deliberately identity-first. On-premises AD DS remains authoritative for domain-joined workloads and Group Policy-bound systems; Entra Connect synchronises it into Microsoft Entra ID, which becomes the control plane for Microsoft 365, Intune, Conditional Access, PIM, guest access, and device compliance. Okta is the strategic SSO provider for SaaS and the federation broker into AWS. Workday drives joiner-mover-leaver automation into Okta over SCIM, so access is provisioned and — critically — deprovisioned from the HR source of truth. AWS access flows through Okta-federated roles; Azure administrative access flows through Entra with RBAC and PIM.
The enforcement point is a Conditional Access engine that evaluates every authentication against user risk, device compliance (Intune plus CrowdStrike posture), location, and application sensitivity, then grants, demands step-up MFA, or blocks. MFA is mandatory for everyone; phishing-resistant MFA is mandatory for privileged roles and high-risk apps. Standing privilege is eliminated: administrators activate roles just-in-time through PIM with approval and a time box, the most sensitive operations run only from Privileged Access Workstations, and a pair of monitored, cloud-only break-glass accounts exist for genuine emergencies. This is the zero-trust trinity in practice — verify explicitly, grant least privilege, assume breach.
Employee digital workplace
The hardest part of “as if you’re on the local network” is delivering the experience without extending flat network trust. The design uses three access channels. Office users get local internet breakout to Microsoft 365 and approved SaaS — protected by identity and endpoint policy rather than by backhauling everything to a data centre — plus direct private-app access. Work-from-home users reach private applications over GlobalProtect VPN with Okta/Entra MFA, and selected apps are modernised toward Microsoft Entra Private Access for per-app, identity-aware connectivity that does not rely on legacy VPN. The third channel is direct SaaS — Microsoft 365, ADP, Workday, ServiceNow, Bitbucket — all behind Okta SSO.
What ties the three channels into one experience is unified identity, consistent DNS resolution across office and VPN, and Conditional Access that is invisible to a compliant user but decisive when risk changes. ADP and every enterprise SaaS app follow the same model: SSO through Okta, mandatory MFA, role-based provisioning from HR attributes, and step-up authentication for payroll and admin actions. Device trust — Intune compliance and CrowdStrike posture — gates access to sensitive systems, and unmanaged devices are restricted to web-only sessions for Microsoft 365 or blocked outright for sensitive workloads. The user never needs to know whether a service runs on-premises, in Azure, or in AWS; application publishing hides the location.
Conditional Access and PIM policy model
Because identity does the heavy lifting, the policy model deserves its own view. Conditional Access is expressed as a decision flow: inputs (user and group, device state, location, sign-in risk, application sensitivity) feed a policy engine that outputs grant, require-MFA, require-compliant-device, block, or session control. The baseline policies are non-negotiable — block legacy authentication, require MFA for all cloud apps, require a compliant or hybrid-joined device for admin portals and sensitive data, enforce phishing-resistant MFA for privileged roles, apply session controls to unmanaged-device Microsoft 365 web access, and demand stronger controls for SAP administration and finance or operations data.
PIM complements Conditional Access on the privileged path. A role is eligible rather than active; an administrator requests activation, an approver grants it, the elevation is time-bound and auto-expires, and every step is written to an immutable audit log. The combination matters: Conditional Access decides whether you may authenticate and from what device, and PIM decides whether — and for how long — you may wield privilege once you are in. Together they remove the two biggest enterprise weaknesses at once: weak authentication and standing administrative access.
Global edge and ingress
Public ingress is a layered funnel that the proposal mandates be third-party and cross-cloud. A request resolves through third-party authoritative DNS with global traffic management, health checks, and automatic failover. It lands on a third-party CDN for global anycast ingress, caching, and TLS. It passes through a third-party WAF that applies OWASP rules in prevention mode, bot management, API security with schema validation, rate limiting, country and ASN allow-deny lists, and reputation filtering. Only the CDN/WAF egress addresses are permitted to reach the origins — origin cloaking that removes the option of bypassing the edge. Regional Azure Application Gateway WAF and AWS ALB-plus-WAF act as a secondary enforcement and app-delivery tier before traffic reaches the application and its private data services.
The win is defence in depth with a single global control point. A new bot-mitigation rule or a country block is applied once at the edge and protects every public application in both clouds, while the regional WAF tier catches anything cloud-specific and keeps origins private. Every transaction, log, and security signal from the edge is forwarded to the SIEM and to Dynatrace, so the same request can be followed from the user’s browser all the way to the database.
Logistics operating model and domains
The landing zone described so far is a foundation, and a foundation is only interesting for what it carries. What the carrier actually runs is not one system but five operating segments, each with its own cadence, regulator, partners, and definition of “an event.” A booking taken at a freight-forwarding branch, a pallet putaway in a distribution centre, a load tendered to a road carrier, a parcel handed to a consignee, and a customs entry lodged with a border authority are five different businesses that share a brand, a balance sheet, and — if the platform is built correctly — one canonical record of the shipment they all touch. The first job of the industry architecture is therefore to decompose the estate by domain before it is decomposed by technology, so each segment lands in the right subscription or account, at the right recovery tier, behind the right isolation boundary, carrying data of the right class. The segmentation is not cosmetic: customs data is regulated and residency-pinned in a way freight rate cards are not, last-mile carries consignee personal data at a scale the forwarding desk never sees, and a warehouse must keep running when the WAN drops in a way a partner API never has to.
The matrix below is the spine of that decomposition — one row per domain, with the applications it runs, the highest data class it handles, its landing zone, its recovery tier, and the isolation boundary that contains a compromise. It is the bridge between the generic landing-zone controls earlier in this document and the logistics-specific systems that follow: every OMS and WMS component in the next two sections inherits its guardrails, tier, and blast-radius boundary from exactly one of these rows.
| Domain | Core applications | Highest data class | Landing zone | Tier | Isolation boundary |
|---|---|---|---|---|---|
| Freight forwarding | Booking & forwarding (CargoWise-class), rating/quotation, consolidation, house/master bill of lading | Confidential | Azure Online prod / AWS Prod OU | Tier-1 | Dedicated subscription + account; commercial rate data segregated |
| Warehousing / 3PL | WMS, slotting, replenishment, 3PL activity-based billing, yard management | Confidential + Restricted (cold-chain custody) | Azure Corp prod + per-site edge zone | Tier-1 | Per-site edge boundary; customer inventory logically partitioned |
| Transportation / fleet | TMS, route & load optimiser, telematics ingest, fleet maintenance | Internal + Restricted (driver PII) | AWS Prod OU (IoT-adjacent) | Tier-1 | Dedicated account; IoT lane segmented from business network |
| Last-mile | Delivery orchestration, driver-app backend, proof-of-delivery, PUDO network | Restricted (consignee PII) | Azure Online prod | Tier-1 | Internet-facing; PII tokenised; high-scale account isolated |
| Customs / trade compliance | Customs filing (CUSDEC/entry), denied-party screening, HS classification, duty & tax | Restricted (regulated) + Confidential | Azure Corp prod (residency-pinned in-region) | Tier-1 | Strong segmentation; in-region only; screening kept auditable |
| Order & visibility (cross-cutting) | OMS, consignment ledger, control tower, customer track-and-trace portal | Confidential + Restricted (PII) | Azure Online / AWS Prod (dual) | Tier-1 | SoR/SoE split; ledger runs single-writer, portals read-only projections |
The final row binds the other five together, and it deserves the architectural care the bank gives its ledger. In logistics the authoritative fact is not “the order” — orders are customer intent, and one order can spawn several shipments, split across depots, or be cancelled before a wheel turns — it is the consignment: a physical, trackable movement with a canonical lifecycle from booked to delivered. The design therefore names one system of record, the consignment ledger, and treats everything a human or partner looks at — the tracking page, the control-tower map, the partner status feed, the driver app — as a system of engagement that projects off that ledger rather than owning any truth of its own. This is the direct logistics analogue of the bank’s “the ledger is the system of record; channels are systems of engagement,” enforced the same way: the ledger is the single writer of consignment state, it appends immutable events, and the SoE surfaces are eventually-consistent read models rebuilt from the stream. The payoff is that a new portal, marketplace, or partner feed subscribes to existing events instead of reaching into a production database, and no volume of read traffic on the SoE tier can corrupt or slow the SoR.
The event backbone that carries those state transitions — Azure Event Hubs and Kafka on the Azure side, Kinesis on the AWS side, stitched into one common-schema bus — is the same backbone the event-driven pipeline patterns elsewhere in the estate describe, applied to shipment lifecycle rather than checkout. Every hop is at-least-once and, across two clouds, potentially out of order, so the ledger tolerates exactly that: idempotent event keys, per-consignment ordering, and a dedup gate before any consumer sees a transition twice. The table below pins the SoR/SoE split down system by system, because the distinction only protects the estate if every component knows which side of the line it sits on and what consistency it may assume.
| System | Role | SoR / SoE | Cloud & tier | Consistency model | Data class |
|---|---|---|---|---|---|
| Consignment ledger | Canonical shipment lifecycle state | SoR | Event Hubs + Cosmos DB, multi-region · Tier-1 | Strong on write — single writer, idempotent append | Confidential / Restricted |
| SAP S/4HANA | Finance, billing, freight invoicing, goods movement | SoR | Certified IaaS, 2 AZs + DR · Tier-1 | Strong (ACID) | Confidential |
| WMS (per site) | Inventory-at-rest for one distribution centre | SoR (site-local) | Warehouse edge + Azure Corp · Tier-1 | Strong locally; store-and-forward to cloud | Confidential |
| TMS | Movement plan, load build, carrier assignment | SoR (movement) | AWS Prod OU · Tier-1 | Strong on plan; eventual on status | Internal |
| Customer track-and-trace portal | Shipment visibility to shippers & consignees | SoE | APIM + Cosmos read model · Tier-1 active-active | Eventually consistent (CQRS projection) | Restricted (PII) |
| Partner API (204/214/EPCIS) | Carrier & partner integration surface | SoE | API Management · Tier-1 | Eventually consistent | Confidential |
| Control tower & exception dashboards | Operations visibility and intervention | SoE | Dynatrace + read models · Tier-2 | Eventually consistent | Internal |
| Driver / delivery app | Last-mile execution and proof-of-delivery | SoE (writes events back) | Azure Online · Tier-1 | Local-first; event write-back to ledger | Restricted (PII) |
None of this integrates with the outside world in JSON alone. Logistics is the last great stronghold of EDI, and a global carrier lives or dies by how cleanly it speaks both dialects — UN/EDIFACT in Europe and Asia, ANSI ASC X12 in North America — alongside the REST and webhook surfaces newer partners expect. The estate runs a hardened B2B/EDI gateway (Logic Apps Standard integration accounts fronted by a partner-managed AS2/OFTP2 endpoint) that translates every inbound interchange into the canonical event model before it reaches the ledger, and reverses that on the way out. Identity down to the message matters as much as it does for users: interchanges are authenticated by AS2 certificates, validated against agreed schemas, and always acknowledged — an unacknowledged 856 or 214 is an invisible failure that surfaces as a “lost” shipment days later. Underneath the messages, GS1 supplies the identity of the physical things — the SSCC license plate on every pallet and carton, the GTIN on every item, the GLN on every location — so a scan in a Rotterdam cross-dock and a status message from a Chicago line-haul carrier refer to the same consignment without a lookup table in the middle.
The diagram reads left to right from the two commercial and three physical domains, through the EDI and API ingress that normalises every partner dialect, into the consignment ledger and event backbone that hold the single truth, and out to the engagement surfaces that project it — with badges on the six places the split is enforced, from the 997/APERAK handshake to the idempotent, single-writer append that keeps the ledger authoritative. The standards table below is the working reference the integration team builds against: each row is a message, the business event it carries, its direction, its transport, and where it lands, so that a new partner onboarding is a matter of mapping to an existing row rather than inventing a bespoke interface.
| Standard / message | Business event | Direction | Transport | Landing / consumer |
|---|---|---|---|---|
| EDIFACT IFTMIN / IFTMBF | Transport booking / firm booking instruction | Inbound (shipper) | AS2 / OFTP2 | EDI gateway → OMS capture |
| EDIFACT IFTSTA | Shipment status / milestone | Outbound (shipper, consignee) | AS2 | Ledger → EDI gateway |
| EDIFACT CUSDEC / CUSRES | Customs declaration / authority response | Both (border authority) | AS2 / portal | Customs domain (in-region) |
| EDIFACT DESADV | Despatch advice (ASN, EDIFACT dialect) | Outbound (consignee) | AS2 | WMS ship-confirm |
| X12 204 / 990 | Motor-carrier load tender / tender response | Out / In (road carrier) | AS2 | TMS ↔ carrier |
| X12 214 | Transportation carrier shipment status | Inbound (carrier) | AS2 | Carrier → ledger status |
| X12 210 | Motor-carrier freight details & invoice | Inbound (carrier) | AS2 | Freight audit → SAP |
| X12 856 | Advance ship notice (ASN) | Outbound (consignee) | AS2 | WMS → consignee |
| X12 940 / 945 | Warehouse shipping order / shipping advice | OMS ↔ 3PL WMS | AS2 / REST | OMS ↔ WMS |
| X12 846 | Inventory inquiry / advice | Outbound (customer) | AS2 | WMS → customer |
| X12 997 / EDIFACT CONTRL | Functional acknowledgement | Both | AS2 | Every interchange (mandatory) |
| GS1 SSCC / GTIN / GLN | Pallet / item / location identity | Labels + internal | GS1-128 barcode | WMS, ASN, EPCIS |
| GS1 EPCIS | Scan / observation events (what-where-when-why) | Internal | REST / event | Event backbone |
Order management and fulfilment
The order-management platform is the demand-facing front of the consignment ledger — the layer that turns a heterogeneous stream of customer intent into validated, priced, sourced, and released consignments the warehouses and carriers execute against. It sits on the SoE side for capture (permissive, multi-channel, always available to take an order) and hands off to the SoR the moment an order becomes a committed consignment. Keeping that boundary crisp is what stops the OMS from becoming a second, competing record of shipment truth: the OMS owns the order until release, the ledger owns the consignment from creation, and the two are joined by events, not a shared database.
Demand arrives through channels that could not differ more in shape, and the platform normalises all of them into one canonical order model at the edge. A small shipper books on the web portal; a large retail customer drops an X12 850 purchase order or a 204 load tender over AS2 on a nightly schedule; a marketplace fires a webhook the instant a buyer checks out; a strategic partner calls the booking API through API Management with an OAuth2 client-credentials token; an operations desk keys a correction into an internal console. Each channel has its own authentication, latency expectation, and failure mode, and the capture tier is sized so the slowest, chattiest channel cannot starve the others.
| Channel | Protocol | Format | Latency profile | Authentication | Landing zone |
|---|---|---|---|---|---|
| Web / customer portal | HTTPS REST | JSON (canonical) | Real-time, interactive | Okta OIDC + step-up MFA | Azure Online |
| EDI (850 PO / 204 tender) | AS2 / SFTP | X12 / EDIFACT | Batch to near-real-time | AS2 certificate + PGP | EDI gateway → integration |
| Marketplace | REST webhook | JSON | Real-time, bursty | HMAC-signed webhook / OAuth2 | Azure Online |
| Partner booking API | REST / GraphQL via APIM | JSON | Real-time | OAuth2 client credentials | API Management |
| Bulk upload | Portal file upload | CSV → canonical | Batch | Portal session (Entra) | Azure Online |
| CSR / call centre | Internal web UI | JSON | Interactive | Entra ID + PIM for overrides | Azure Corp |
Once captured, an order is orchestrated through a saga rather than a monolithic transaction, because fulfilment spans systems that fail independently and cannot be locked together. A durable orchestrator — Azure Durable Functions, AWS Step Functions — drives the order through validation, rating, availability, allocation, and release, holding a compensating action for every step so a late failure unwinds cleanly instead of leaving a half-committed shipment. The systems inventory below is the OMS as actually deployed: each component, its function, the compute it runs on, its tier, its HA posture, and the data class it handles. Note that the consignment ledger appears here as the point of hand-off — the OMS writes the first authoritative consignment event and thereafter reads state back like every other engagement surface.
| System | Function | Cloud / SKU | Tier | HA posture | Data class |
|---|---|---|---|---|---|
| Order Capture API | Ingest, canonicalise, validate orders | Azure AKS / AWS EKS | Tier-1 | Multi-AZ + warm DR | Confidential |
| Order Orchestrator | Saga / state machine over fulfilment | Durable Functions / Step Functions | Tier-1 | Regional active-active | Confidential |
| ATP / Inventory service | Availability check + reservation | Azure SQL Business Critical / Aurora | Tier-1 | Zone-redundant, auto-failover | Confidential |
| Rating & quotation | Rate cards, dims, accessorials, surcharge | AKS + Redis cache | Tier-1 | Zone-redundant, cached | Confidential |
| Allocation / sourcing | DC and carrier selection, split logic | AKS / EKS | Tier-1 | Multi-AZ | Confidential |
| Consignment ledger | Canonical shipment state (event store) | Event Hubs + Cosmos DB / Kinesis + DynamoDB | Tier-1 | Multi-region, idempotent | Confidential / Restricted |
| Exception management | Hold, resolve, re-drive stuck orders | Functions + Service Bus | Tier-2 | Zone-redundant queues | Internal |
| Customer visibility API | Track-and-trace read model (SoE) | APIM + Cosmos read model | Tier-1 | Active-active | Restricted (PII) |
Two OMS decisions carry disproportionate weight, and both are about promising. The first is available-to-promise: before accepting an order the platform must know whether it can be filled, and from where. ATP is on-hand minus already-allocated plus scheduled-inbound within the promise window, evaluated across a network of distribution centres rather than a single building, so a stockout in one DC sources from another instead of failing the order. The reservation that follows is the dangerous part — under concurrent demand two orders can each read “one unit available” and both commit it — so it is a conditional write on the inventory record (the ledger’s idempotency discipline), turning an oversell into a clean, retryable failure rather than a promise the warehouse cannot keep. The second is rating: freight is priced on dimensional as much as actual weight, and a wrong quote is a margin leak on every shipment that carries it. The engine takes chargeable weight as the greater of actual and dimensional weight (L × W × H ÷ the carrier’s divisor), applies the contract or published rate for the origin-destination zone, then layers accessorials — residential, liftgate, inside delivery — and a fuel surcharge indexed to a published rate. The inputs are worth tabulating, because a rating dispute always comes down to which of them was wrong.
| Rating / ATP input | Source | Example value | Effect on outcome |
|---|---|---|---|
| Actual weight | Pack-station scale (WMS) | 14.2 kg | Floor for chargeable weight |
| Dimensional weight | Dimensioner (L×W×H ÷ divisor) | 40×30×25 cm ÷ 5000 = 6.0 kg | Chargeable = max(actual, dim) |
| Zone | Origin GLN → destination postcode | Zone 4 | Selects rate-card row |
| Contract rate | Customer rate card | £14.90 base | Overrides published rate |
| Accessorials | Order flags (residential, liftgate) | +£3.50 | Added to base |
| Fuel surcharge | Published index | 14.5% | Multiplier on linehaul |
| On-hand − allocated + inbound | ATP across DC network | 3 units at DC-LHR | Determines sourcing DC |
The lifecycle that ties capture, rating, allocation, and execution together is expressed as events on the backbone, not as calls between services, which is what lets the WMS, TMS, invoicing, and customer visibility all react to the same transition without the OMS knowing they exist. The table below is the order-to-delivery event flow — each event, the service that emits it, the subject it is published under, and the consumers that act on it — and it is the contract every downstream team codes against.
| Event | Producer | Subject / topic | Primary consumers | SLA |
|---|---|---|---|---|
order.captured |
Capture API | orders.captured |
Orchestrator, audit | < 1 s |
order.validated |
Orchestrator | orders.validated |
Rating | < 2 s |
order.rated |
Rating engine | orders.rated |
Allocation, customer quote | < 2 s |
consignment.created |
Orchestrator | consignment.created |
Ledger, WMS, TMS | < 1 s |
consignment.allocated |
Allocation | consignment.allocated |
WMS (wave), TMS (plan) | < 3 s |
consignment.released |
Orchestrator | consignment.released |
WMS (940 shipping order) | < 2 s |
consignment.shipped |
WMS | consignment.shipped |
Ledger, ASN 856, invoicing | < 2 s |
consignment.status.changed |
TMS / carrier 214 | consignment.status |
Visibility API, notifications | < 5 s |
consignment.delivered |
Last-mile / POD | consignment.delivered |
Ledger, invoicing, customer | < 5 s |
consignment.exception |
Any producer | consignment.exception |
Exception mgmt, ServiceNow | < 3 s |
The canonical event on that backbone is worth showing in full, because its shape is what makes two clouds and a dozen consumers agree on one truth. Every consignment transition is published as a CloudEvents-wrapped message whose data carries GS1 identity, an X12-214-aligned status code, and a GLN-coded location, so that an EDIFACT IFTSTA, an X12 214, and a portal update all render from the same fact:
{
"specversion": "1.0",
"type": "com.globallogistics.consignment.status.changed",
"source": "/tms/eu-west/linehaul",
"id": "018f7c2a-9b41-7e02-a3d6-2f5c1e77d901",
"subject": "CONS-778812",
"time": "2026-07-09T14:35:02Z",
"datacontenttype": "application/json",
"data": {
"consignmentId": "CONS-778812",
"sscc": "00354123450000000019",
"statusCode": "X6", // X12 214: "In-transit"
"statusReason": "AF", // "Carrier departed"
"locationGln": "5412345000176",
"locationName": "Hub LHR-Gateway",
"occurredAt": "2026-07-09T14:34:58Z",
"carrierScac": "GLEX",
"sequence": 7, // per-consignment ordering
"idempotencyKey": "CONS-778812:7:X6"
}
}
Exceptions are not an afterthought in logistics; they are a first-class flow, because a meaningful fraction of consignments hit a hold, a re-route, an address correction, or a customs stop before they deliver. The exception service subscribes to the consignment.exception stream, opens or enriches a ServiceNow incident against the affected consignment’s configuration item, and either auto-resolves (a transient carrier-API timeout re-driven from the queue) or routes to a human queue (a customs documentation gap). The table below is the exception playbook — the trigger, how it is detected, the automated action, and the owner — so the difference between a shipment that self-heals and one that pages someone is a policy decision rather than an accident.
| Exception | Trigger | Detection | Automated action | Owner |
|---|---|---|---|---|
| Oversell / stock-out | Reservation conditional-write fails | ATP service returns conflict | Re-source from alternate DC; else backorder event | OMS / Inventory |
| Rating failure | No rate-card row for zone | Rating engine no-match | Fall back to published rate; flag for review | OMS / Commercial |
| Tender rejected | X12 990 declines the load | 990 parsed at gateway | Re-tender to next carrier in routing guide | TMS |
| Address / postal invalid | Validation or carrier API 4xx | Capture / manifest step | Address-cleanse; hold + CSR queue if unresolved | Last-mile |
| Customs stop | CUSRES query or missing docs | Customs domain | Hold consignment; open documentation task | Customs / Trade |
| Carrier-API timeout | Manifest call exceeds SLA | Circuit breaker / DLQ | Re-drive from queue with idempotency key | Platform / WMS |
The diagram traces one order left to right: from the four capture channels, into the AKS capture API and durable orchestrator, through the ATP reservation and rating that turn intent into a priced promise, into allocation and the first authoritative ledger append, and out to the WMS release and TMS tender — with badges on the six control points that keep the flow safe, from the functional-acknowledgement handshake on inbound EDI to the conditional reservation that makes oversell a retry rather than a broken promise.
Warehouse management and automation
If the OMS is where the business promises, the warehouse is where it delivers — and the warehouse has a property no other domain shares: it must keep running when the cloud does not. A distribution centre with a severed WAN link is still a building full of people, trucks, and automation that has to receive, store, pick, pack, and ship, and a WMS that stops because it cannot reach a control plane in another region has failed at the one job that cannot pause. The design therefore treats each site as a local system of record for inventory-at-rest on a resilient edge, synchronising to the cloud ledger through store-and-forward, rather than as a thin client of a cloud service. Everything else in this section — the process flow, the automation integration, the EDI and carrier interfaces — is built on that foundation of site autonomy.
The WMS runs the four walls as a directed, event-emitting process. Inbound begins when an ASN (X12 856 or EDIFACT DESADV) arrives ahead of the truck, so the dock knows what to expect before the doors open; receiving reconciles the physical unload against that ASN by scanning the SSCC license plate on each pallet, and any discrepancy is captured then and there rather than at cycle count. Putaway is directed by slotting rules that place fast-moving GTINs in golden-zone pick faces and bulk in reserve; picking is organised into waves cut by carrier cut-off and route; pack cartonises, weighs, and labels; and ship manifests the consignment, calls the carrier for a label and tracking number, and emits the consignment.shipped event that fires the ledger, the ASN to the consignee, and invoicing. The table below is that flow with its triggers, automation, and captured data — the operational contract between the WMS, the automation on the floor, and the events that reach the cloud.
| Process | Trigger | Key steps | Automation involved | Data captured |
|---|---|---|---|---|
| Inbound / receiving | ASN 856 / 943 arrives | Dock check-in, unload, scan SSCC, verify vs ASN | RF scanners, dock-door sensors | GRN, discrepancy, quantity |
| Putaway | Receipt confirmed | Directed putaway by slotting rule | AS/RS, RF forklift terminals | Bin location assignment |
| Slotting | Velocity re-eval (scheduled) | ABC re-slot, golden-zone assignment | Slotting analytics | Bin velocity class |
| Replenishment | Pick-face min/max breach | Top-off from reserve to pick face | Conveyor, AGV/AMR | Replen task, moved qty |
| Wave planning | Order release / carrier cut-off | Batch orders into waves by route/carrier | Wave & labour engine | Wave, workload balance |
| Picking | Wave / task assigned | Directed pick, confirm by scan/light/voice | Pick-to-light, robots, RF | Pick confirm, short-pick |
| Pack | Pick complete | Cartonise, weigh, dimension, label | Auto-bagger, scale, dimensioner, printer | Carton, weight, dims, SSCC |
| Ship / manifest | Pack complete | Manifest, carrier label, ASN, load sequence | Carrier API, print-and-apply, sortation | Tracking #, 856, manifest |
| Cycle count | Schedule / trigger | Count bins, reconcile to system | RF terminals | Count variance, adjustment |
| Yard / dock | Appointment / gate event | Gate-in/out, dock assign, trailer move | YMS, ANPR cameras | CODECO / gate event |
Automation is where the warehouse stops being a database problem and becomes a real-time control problem, and it is the sharpest reason the site cannot depend on a distant cloud. Beneath the WMS sits a warehouse control / execution system (WCS/WES) that speaks to physical equipment on hard real-time deadlines a cross-region round-trip would blow. The WMS decides what — pick these lines for this wave — and hands discrete tasks to the WCS, which decides how the crane, conveyor, or robot does it; the two are joined by an on-site message interface, not a chatty API across the WAN. The table below maps the automation subsystems to how they connect and where they run, and the recurring answer in the last column is the same: Tier-1, at the edge, on the site LAN.
| Subsystem | Vendor class (example) | Protocol | Integrates via | Runs at |
|---|---|---|---|---|
| AS/RS (crane, shuttle) | Dematic, SSI Schäfer | OPC-UA / message queue | WMS → WCS task interface | Tier-1 edge |
| Conveyor & sortation | Honeywell Intelligrated | PLC / SCADA, MQTT | WCS / WES | Tier-1 edge |
| Robotics (AMR, goods-to-person) | Locus, AutoStore, 6 River | REST / gRPC + MQTT | WES | Tier-1 edge |
| Pick-to-light / put-to-light | Lightning Pick, Kardex | Serial / Ethernet to controller | WMS | Tier-2 edge |
| RF / handheld scanners | Zebra (Android) | HTTPS over site Wi-Fi | WMS session | Tier-1 edge |
| Print-and-apply / labelling | Zebra (ZPL) | TCP 9100 / IPP | WMS / carrier label service | Tier-2 edge |
| Weigh & dimensioning | Mettler Toledo, Cubiscan | Serial / REST | Pack station | Tier-2 edge |
| Yard cameras / ANPR | — | RTSP / REST | YMS | Tier-2 edge |
The edge-resilience design is therefore explicit rather than assumed. Each distribution centre runs a local control plane — Azure Arc-enabled Kubernetes (or AWS EKS Anywhere / Outposts where the site is AWS-aligned) hosting the WMS execution services, a local database as the authoritative store for inventory-at-rest (SQL Server Always On or PostgreSQL with a hot standby in the same building), and a local store-and-forward broker (Arc data services Service Bus, or an on-site Kafka/MQTT broker) that buffers every event when the WAN is down and replays it in order on reconnect. With the link healthy the site streams events to the cloud ledger in near real time and pulls new orders, ATP, and slotting down; when it drops, the site falls into a defined degraded mode rather than an undefined outage. The matrix below is the contract for that degradation — what each capability does with the WAN up, what it does with it down, and how it reconciles on recovery — the difference between “the site keeps shipping” and “the site stops.”
| Capability | WAN up (normal) | WAN down (degraded) | On reconnect |
|---|---|---|---|
| Receiving / putaway | Full, events streamed live | Full — ASN cached locally, scans stored | Buffered events replay to ledger in order |
| Pick / pack / ship | Full, live ledger updates | Full — local WMS + WCS authoritative | Ship events + 856 flush from store-and-forward |
| Carrier label / manifest | Live carrier API call | Cached rates + queued manifest; local label print | Manifest calls re-driven with idempotency key |
| New order intake | Real-time from OMS | Paused — no new waves from cloud | Backlog pulls on link restore |
| ATP / cross-DC sourcing | Live network availability | Local on-hand only, no network sourcing | Re-syncs on reconnect |
| Customer visibility (SoE) | Live projection | Stale until reconnect | Rebuilds from replayed events |
That autonomy is not free-floating; it is a concrete footprint the site build ships with — small enough to fit a distribution-centre comms room and standardised so every DC recovers the same way. The table below is that per-site edge inventory: the components each warehouse runs locally to stay authoritative, and how each rejoins the cloud when the link returns.
| Edge component | Technology | Local role | Sync to cloud |
|---|---|---|---|
| Edge cluster | Azure Arc-enabled AKS / EKS Anywhere (3-node) | Hosts WMS execution + WCS adapters | GitOps config pull; heartbeat |
| Local inventory DB | SQL Server Always On / PostgreSQL + hot standby | Authoritative inventory-at-rest | Change feed → ledger (store-fwd) |
| Store-and-forward broker | Arc Service Bus / on-site Kafka / MQTT | Buffer events during WAN loss | Ordered replay on reconnect |
| Identity cache | Entra ID / Okta token cache + on-site PAW | Auth when the IdP is unreachable | Re-validates on reconnect |
| RF & label network | Dedicated Wi-Fi SSID/VLAN, ZPL print-and-apply | Scanner and labelling plane | Local only |
| Local observability | Dynatrace ActiveGate (edge) | Buffer telemetry, local dashboards | Forwards on reconnect |
Two pieces of real configuration make the warehouse’s external contracts concrete. The first is the Advance Ship Notice the WMS transmits to the consignee the moment a load is confirmed — an X12 856 whose whole value is its hierarchical structure: a Shipment loop that nests Order, then Tare (pallet), then Pack (carton), then Item, so the receiver can put away by pallet without opening it and reconcile down to the unit if they need to. The MAN segments carry the GS1 SSCC license plates generated at the pack station, which is what lets a single scan at the consignee’s dock match a whole pallet against this ASN:
ST*856*0001~
BSN*00*000012345*20260709*1435*0001~ Beginning of hierarchical transaction
HL*1**S~ Shipment level
TD1*CTN25*12****G*350*LB~ 12 cartons, gross 350 lb
TD5**2*GLEX*M*LTL~ Carrier = SCAC GLEX, motor, LTL
REF*BM*BOL00098765~ Bill of lading number
DTM*011*20260709~ Shipped date
N1*ST*ACME DISTRIBUTION*92*DC42~ Ship-to (buyer DC 42)
N1*SF*GLOBAL LOGISTICS DC-LHR*92*GB-LHR-01~ Ship-from (our DC)
HL*2*1*O~ Order level (child of shipment)
PRF*PO554433~ Purchase order 554433
HL*3*2*T~ Tare / pallet level
MAN*GM*00354123450000000019~ SSCC-18 pallet license plate
HL*4*3*P~ Pack / carton level
MAN*GM*00354123450000000026~ SSCC-18 carton license plate
HL*5*4*I~ Item level
LIN**UP*00812345600012~ GTIN of the item
SN1**24*EA~ 24 each in this carton
SE*20*0001~ 20 segments, control 0001
The gateway wraps that transaction in its ISA/GS envelope, transmits it over AS2, and expects a 997 functional acknowledgement back; an ASN that is never acknowledged is escalated like any unacknowledged status event, because a receiver expecting a pallet no ASN described is a delay waiting to happen. The second piece of real config is the WMS-to-carrier call that produces the label the ship step depends on. At manifest the WMS posts the consignment to the carrier’s shipping API and gets back a tracking number and a print-ready ZPL label; the request carries an Idempotency-Key set to the consignment id, so a retried call after a timeout never books — or bills — the shipment twice:
POST /v1/shipments HTTP/1.1
Host: api.carrier.example.com
Authorization: Bearer {oauth2_access_token}
Idempotency-Key: CONS-778812
Content-Type: application/json
{
"serviceCode": "EXPRESS_1000",
"reference": "CONS-778812",
"shipFrom": { "gln": "5412345000015", "name": "Global Logistics DC-LHR",
"postcode": "TW6 2GA", "country": "GB" },
"shipTo": { "name": "ACME Distribution", "postcode": "M1 4WP", "country": "GB" },
"parcels": [ { "sscc": "00354123450000000026", "weightKg": 14.2,
"dimsCm": [40, 30, 25] } ],
"labelFormat": "ZPL_203DPI",
"codRequired": false
}
{
"shipmentId": "SHP-9F2C41A8",
"trackingNumber": "GLEX778812004417",
"parcels": [ { "sscc": "00354123450000000026",
"trackingNumber": "GLEX778812004417",
"labelZpl": "^XA...^XZ" } ],
"estimatedDelivery": "2026-07-10T10:00:00Z",
"rate": { "currency": "GBP", "amount": 18.40 }
}
The tracking number returned there is written straight to the consignment ledger and surfaces on the customer portal within seconds, the ZPL streams to the print-and-apply head on the sortation line at 203 dpi, and the whole exchange is captured for the freight audit that later reconciles the carrier’s X12 210 invoice against this quoted rate. The integration surface is broader than these two flows, and the table below closes the section by naming every interface the WMS holds — to the OMS above it, the TMS beside it, SAP behind it, the carriers in front, and the automation beneath — because a warehouse is defined as much by its edges as by its four walls.
| Interface | Partner system | Protocol / message | Direction | Cadence |
|---|---|---|---|---|
| WMS ↔ OMS | Order management | X12 940 in / 945 out, or REST | Bidirectional | Event-driven |
| WMS ↔ TMS | Transportation | Load, manifest, carrier assignment (REST) | Bidirectional | Per shipment |
| WMS ↔ SAP | S/4HANA | Goods movement (IDoc / BAPI, MIGO) | Bidirectional | Per posting |
| WMS ↔ carrier | Carrier shipping API | REST (rate, label, manifest, track) | Outbound | Per parcel |
| WMS ↔ consignee | Customer | X12 856 ASN, 846 inventory | Outbound | Per shipment / daily |
| WMS ↔ WCS/WES | On-site automation | OPC-UA / MQTT / message queue | Bidirectional | Real-time (edge) |
| WMS ↔ event backbone | Consignment ledger | CloudEvents over Event Hubs / Kafka | Outbound (store-fwd) | Near real-time |
The diagram follows a consignment left to right — receiving and SSCC verification, storage and automation, wave/pick/pack/ship, and the yard — with the warehouse-edge node beneath carrying the resilience story: an Arc-enabled cluster and local database that keep the four walls authoritative, and a store-and-forward buffer that replays every event to the cloud ledger when the WAN returns. The badges mark the six points where the design earns its keep, from ASN-versus-GRN reconciliation at the dock to the WAN-drop autonomy that lets the site keep shipping when everything upstream goes dark.
Transportation management and fleet telematics
If IoT is where the estate meets the physical world, transportation management is where the physical world meets money — every load planned badly, every empty mile driven, every carrier over-paid, and every reefer that drifts out of temperature shows up directly in margin. The Transportation Management System (TMS) is therefore treated as a Tier-1 workload with the same availability envelope as the customer platforms it feeds: it runs active-active across two availability zones behind the regional Application Gateway WAF v2 and AWS ALB, its ledger of loads and rates lives on zone-redundant Azure SQL Business Critical with an asynchronous warm standby in the paired DR region, and it never talks to a carrier, a warehouse, or a truck except over a governed, event-driven path. What follows is the operational core of that domain — how a load is planned, priced, tendered, dispatched, tracked, and closed — and the real-time telemetry pipeline that keeps the fleet observable from the moment a driver accepts a load to the moment they capture proof of delivery.
The TMS is not one program but a stack of decisions, each owned by a distinct capability and each emitting an event the rest of the estate can subscribe to. Load planning and optimisation consolidates orders into loads, respecting weight, cube, hazmat segregation, temperature class, and delivery windows, then hands the candidate loads to a solver that minimises total landed cost across the available carrier lanes. Carrier selection and rating scores each lane against a rate matrix — contract rates, spot rates, fuel surcharges, accessorials — and a service-level and sustainability weighting, so the cheapest carrier is not chosen when it blows the delivery window or the CO2 budget. Only once a load is planned and priced does it move to tendering, and only once tendered-and-accepted does it move to dispatch. The table below is the capability spine: each row is a decision the TMS makes, the system of record that owns it, the cloud it runs on, and the artefact it emits that everything downstream keys off.
| Capability | Decision it makes | System of record | Cloud / platform | Artefact emitted |
|---|---|---|---|---|
| Load planning & consolidation | Which orders combine into which loads | TMS planning module | Azure AKS (D8s_v5 pool) | load.planned event + load ID |
| Route optimisation | Stop sequence, lane, mode, ETA | Optimisation solver | Azure Functions Premium + solver | Optimised route + planned ETA |
| Carrier selection & rating | Which carrier, at what rate | Rating engine + rate tables | Azure SQL Business Critical | Ranked carrier list + landed cost |
| Tendering | Offer/accept with the chosen carrier | B2B/EDI (Logic Apps Standard) | Azure Logic Apps + Service Bus | EDI 204 out / 990 back |
| Dispatch | Bind driver, tractor, trailer to the load | Dispatch module | Azure AKS | load.dispatched + asset IDs |
| Execution & telematics | Where the load is, in what condition | IoT + streaming | Azure IoT Hub / AWS IoT Core | EDI 214 milestones + telemetry |
| Settlement | What the carrier is owed | Freight settlement | Azure SQL + SAP | freight.rated + invoice match |
Tendering is where the TMS stops being an internal optimiser and starts speaking the lingua franca of freight, which is still overwhelmingly EDI — X12 in North America, EDIFACT elsewhere — carried over AS2 or increasingly over carrier REST APIs. The estate standardises B2B onto Azure Logic Apps Standard with the Integration Account for X12/EDIFACT schemas and trading-partner agreements, because it runs inside the VNet, does stateful correlation, and gives every message a run history that ties back to a ServiceNow change. A load tenders outbound as a 204 (Motor Carrier Load Tender); the carrier must answer with a 990 (Response to Load Tender) carrying accept or decline; execution status flows back as a stream of 214 (Transportation Carrier Shipment Status) messages; and the carrier bills with a 210 (Motor Carrier Freight Details and Invoice) that freight settlement matches against the rated load. The table pins the transaction set the domain actually implements, in both directions.
| Transaction | Name | Direction | Fires when | Consumed by |
|---|---|---|---|---|
| EDI 204 | Motor carrier load tender | TMS → carrier | Load planned, carrier selected | Carrier TMS |
| EDI 990 | Response to load tender | Carrier → TMS | Carrier accepts / declines | TMS tendering (auto-retender on decline) |
| EDI 214 | Shipment status message | Carrier → TMS | Each milestone (pickup, in-transit, delivery) | Track-and-trace platform |
| EDI 210 | Freight details & invoice | Carrier → TMS | Carrier bills the load | Freight settlement / SAP |
| EDI 856 | Advance ship notice (ASN) | Shipper ↔ consignee | Goods dispatched | WMS / consignee receiving |
| EDIFACT IFTMIN | Instruction message | TMS → carrier | International forwarding leg | Forwarder / ocean carrier |
| EDI 997 | Functional acknowledgement | Both | Any interchange received | The sender (delivery proof) |
The diagram reads left to right as the load’s own lifecycle: the TMS plans and rates a load and the optimiser decides carrier and route (badge 1) before it tenders as an EDI 204 and waits for the 990 accept (badge 2); dispatch binds a driver and asset; then trucks and containers stream telematics over the IoT lane, onboarding with an X.509 certificate through the Device Provisioning Service (badge 3) and landing on IoT Hub / IoT Core and the Kinesis/Event Hubs stream; the hot path runs geofence rules in under five seconds (badge 4) and detects cold-chain excursions (badge 5); and ePOD closes the loop idempotently (badge 6) into the lakehouse and Dynatrace.
Execution is where telematics takes over from EDI, and the ingest path is deliberately split the same way the wider IoT lane is: AWS IoT Core carries fleet-scale vehicle telemetry — GPS, speed, engine and ELD (Electronic Logging Device) hours-of-service, fuel, odometer — while Azure IoT Hub carries the container and control-tower telemetry that integrates with business processes, including reefer temperature and door sensors. Every device holds a certificate-based identity, supports store-and-forward for the long stretches where cellular or LPWAN coverage drops, and is provisioned through DPS (Azure) or the registry (AWS) so an un-attested device can never publish a position. The systems table below is the concrete inventory of the domain — the TMS and fleet platforms, what each is for, the cloud and SKU it runs on, and what it integrates with — so an operator can see the whole moving estate at a glance.
| System | Role | Cloud / SKU | Integrates with |
|---|---|---|---|
| TMS core (planning, rating, dispatch) | Plan, price, tender, dispatch loads | Azure AKS D8s_v5, Azure SQL Business Critical | SAP (order/settlement), carriers (EDI), track-and-trace |
| Route optimisation solver | Minimise landed cost, sequence stops | Azure Functions Premium EP2 | TMS core, map/traffic APIs, telematics ETA feedback |
| B2B / EDI gateway | X12 & EDIFACT 204/990/214/210 | Azure Logic Apps Standard + Integration Account | Carrier VANs/APIs over AS2, TMS core, SAP |
| Telematics ingest (fleet) | Vehicle GPS/ELD/fuel at scale | AWS IoT Core + Kinesis Data Streams | Fleet devices (mTLS), Managed Flink, Timestream |
| Telematics ingest (control tower) | Container / reefer / door / control | Azure IoT Hub S3 tier + DPS | Container trackers (mTLS), Stream Analytics, ADX |
| Fleet & reefer analytics | Fuel, utilisation, cold-chain, CO2 | Databricks on ADLS Gen2 + Timestream | Lakehouse governed band, sustainability reporting |
| ePOD (proof of delivery) | Capture scan, signature, geo, photo | Azure App Service + Cosmos DB | Driver mobile app (UEM), billing, customer portal |
The real-time telemetry pipeline is the hot path that turns a raw device message into an actionable event inside the five-second SLA the architecture commits to. Ingest terminates mutual TLS and validates device identity; a decode-and-enrich stage normalises vendor payloads and joins the deviceId to its current shipmentId from the device twin; a stateful streaming stage runs the geofence, route-deviation, and threshold rules; and a routing stage forks each detected event to the hot store (live position for the control tower), to the notification path (dispatcher and consignee), and to the lakehouse for history. The table breaks the pipeline into its stages, the service that runs each on either cloud, the transform it performs, and the latency budget it holds.
| Stage | Azure service | AWS service | Transform | Budget |
|---|---|---|---|---|
| Ingest & authenticate | IoT Hub + DPS (mTLS, X.509) | IoT Core (mTLS, registry) | Terminate TLS, verify device, accept/queue | < 500 ms |
| Decode & enrich | Azure Functions | Lambda | Normalise payload, join twin → shipmentId | < 800 ms |
| Stream detect | Stream Analytics (SU-scaled) | Managed Service for Apache Flink | Geofence, deviation, threshold, watermarking | < 2 s |
| Route & fan-out | Event Hubs + Service Bus | Kinesis + SNS/SQS | Fork to hot store, notify, lakehouse | < 1 s |
| Hot store | Cosmos DB (session consistency) | DynamoDB / Timestream | Upsert live position, last-known state | < 700 ms |
Because the geofence and cold-chain rules are the events a dispatcher actually acts on, they are worth stating as concrete rule config rather than prose. The AWS IoT Core topic rule below selects only the vehicle messages that matter and routes them to Kinesis; the Azure IoT Hub route promotes reefer excursions to the hot path; and the Azure Stream Analytics query is the geofence itself, using ST_WITHIN against a per-shipment polygon so a value of zero means the asset has left its corridor.
-- AWS IoT Core rule: forward only actionable ELD/telematics to Kinesis
SELECT deviceId, ts, lat, lon, speed, odometer, fuelPct, reeferC
FROM 'fleet/+/telematics'
WHERE reeferC > 8 OR speed > 105 OR fuelPct < 10
-- Action: kinesis (stream = fleet-telematics-hot, partitionKey = deviceId)
# Azure IoT Hub message route — reefer excursion to the hot path
Name : coldchain-breach
Source : DeviceMessages
Endpoint : eh-telematics-hot (Event Hubs, 8 partitions)
Condition : $body.metric = 'reeferTemp'
AND $body.celsius > $twin.desired.coldChain.maxC
AND $connectionDeviceId LIKE 'reefer-%'
Enrich : shipmentId = $twin.tags.shipmentId
-- Azure Stream Analytics: geofence / route-deviation, < 5 s end to end
SELECT t.deviceId, t.shipmentId, t.eventTime, g.geofenceId, 'CORRIDOR_EXIT' AS event
INTO [alerts-hot]
FROM [telematics] t TIMESTAMP BY t.eventTime
JOIN [geofences] g ON t.shipmentId = g.shipmentId
WHERE ST_WITHIN(CreatePoint(t.lat, t.lon), g.polygon) = 0
The detection rules the pipeline enforces are few but load-bearing, and each one names an action rather than merely a threshold — a breach is a business event with an owner, not a chart that someone might notice. The final table of this section is the rule book the fleet runs against: the rule, the signal and condition that trips it, the action it fires, and the SLA it holds. ePOD closes the loop idempotently — a driver on a dead-zone road re-syncs the same delivery, and the exactly-once effect guarantees one delivery record and one billing trigger — which is the seam that ties fleet execution into the order-to-delivery workflow the third section traces end to end.
| Rule | Signal & condition | Action | SLA |
|---|---|---|---|
| Corridor exit / deviation | GPS outside per-shipment geofence polygon | Alert dispatcher, recompute ETA | < 5 s |
| Cold-chain excursion | Reefer temp > twin maxC for > 5 min |
Open ServiceNow case, alert consignee | < 5 s |
| Harsh-driving / safety | Accel/brake threshold, ELD HOS near limit | Safety flag, driver + fleet manager notify | < 10 s |
| Fuel / theft anomaly | Fuel drop with engine off, off-route stop | Fraud flag, dispatcher review | < 30 s |
| ePOD capture | Scan + signature + geo at delivery point | Idempotent close, trigger billing | Exactly once |
| Dwell / detention | Asset stationary at stop > contracted free time | Accrue accessorial, notify shipper | < 60 s |
Customs, trade compliance and track-and-trace
A shipment that crosses a border twice touches two customs regimes, a fistful of party and product screens, and a documentation pack that must be right before the cargo arrives rather than after — and the same shipment, seen from the customer’s side, is a single question repeated hourly: where is my freight and when will it land? Those two faces of the same movement — regulatory clearance and customer visibility — are built on one shared spine: a stream of milestone events, normalised to a common model, that both files with the authorities and paints the customer timeline. This section covers customs filing, denied-party and sanctions screening, HS classification and duty, the documentation pack, and the multi-source track-and-trace platform that turns raw events into a single shipment story and a proactive ETA.
Customs filing is a set of pre-arrival obligations, and the estate treats each regime as an integration with a hard deadline rather than a form someone fills in. Into the United States, cargo files through ACE (Automated Commercial Environment) with an ISF (“10+2”) before an ocean lade and an entry on arrival, and exports file through AES (Automated Export System). Into the EU, the security filing is ICS2 (Import Control System 2) carrying the ENS (Entry Summary Declaration) before the goods reach the first point of entry. Every filing is assembled from the same event and master data the track-and-trace platform holds, submitted over a governed B2G connector, and — critically — tracked to an accepted response, because a submitted-but-rejected filing holds cargo just as surely as no filing at all. The table below is the filing map the compliance domain runs against.
| Regime / message | Authority | System | Cloud / connector | Trigger | Deadline |
|---|---|---|---|---|---|
| ISF (10+2) | US CBP | ACE filing | Azure Logic Apps (B2G AS2/API) | Booking confirmed, pre-lade | ≥ 24 h before vessel load |
| Customs entry | US CBP | ACE entry / broker | Azure Logic Apps + broker API | Arrival / pre-arrival | On/most before arrival |
| ENS (ICS2) | EU customs | ICS2 declaration | Azure Logic Apps (ICS2 STI) | Pre-loading / pre-arrival | Mode-specific, pre-arrival |
| Export declaration | US CBP | AES (EEI) | Azure Logic Apps | Export booking | Pre-departure |
| Transit / T1 | EU / national | NCTS connector | Azure Logic Apps | Bonded movement start | Before movement |
| Duty & tax calc | Multiple | Landed-cost engine | Azure Functions + tariff data | HS + value + origin known | Before entry |
Filing is only safe if the parties and the products are clean, so screening runs before a shipment moves, not as an afterthought. Denied-party and sanctions screening checks consignor, consignee, notify party, and end user against consolidated lists (OFAC SDN, EU consolidated, UN, and carrier-specific) on every booking and again on any party change; a hit holds the shipment and routes it to compliance for adjudication rather than merely logging a warning. HS (Harmonised System) classification assigns the tariff code that drives duty, tax, licence, and admissibility, increasingly with an ML-assisted classifier whose suggestions a licensed broker confirms. Duty and tax are computed by a landed-cost engine from HS code, customs value, and origin, feeding both the quote and the entry. The controls table states what each screen checks, the source it checks against, and what a hit does.
| Control | What it checks | Source / tool | On a hit |
|---|---|---|---|
| Denied-party / sanctions | Consignor, consignee, notify, end user | OFAC SDN, EU/UN lists, screening API | Block shipment, route to compliance |
| Restricted-party re-screen | Any party change mid-shipment | Same lists, event-triggered | Re-hold, re-adjudicate |
| HS classification | Correct tariff code for the goods | ML classifier + broker confirm | Reclassify, recompute duty |
| Licence / admissibility | Export licence, dual-use, embargo | Trade-content database | Hold pending licence |
| Duty & tax | Landed cost, VAT, preferential origin | Tariff engine + FTA rules | Recompute, flag under/over |
| Document completeness | Invoice, packing list, CoO, cert | Doc-gen + checklist | Block filing until complete |
The visibility half of the domain rests on GS1 EPCIS (Electronic Product Code Information Services), the standard that lets a scan from any system — a WMS pick, a carrier gate-in, a telematics geofence — be described in the same four dimensions and stacked onto one timeline. Every event answers what (the EPC or SSCC identifying the physical unit), where (the readPoint and business location as GS1 SGLNs), when (the event time and time-zone offset), and why (the bizStep and disposition — departing, in transit, receiving, in progress). Normalising to EPCIS is what makes the track-and-trace platform a unified timeline rather than a pile of vendor formats, and it is the model the milestone store persists. The dimension table below maps the concept to the concrete EPCIS field and a real value.
| EPCIS dimension | Field | Example value |
|---|---|---|
| What (object) | epcList / SSCC |
urn:epc:id:sscc:0614141.1234567890 |
| Why (step) | bizStep |
urn:epcglobal:cbv:bizstep:departing |
| State | disposition |
urn:epcglobal:cbv:disp:in_transit |
| Where (read) | readPoint (SGLN) |
urn:epc:id:sgln:0614141.00777.0 |
| Where (business) | bizLocation (SGLN) |
urn:epc:id:sgln:0614141.00888.0 |
| When | eventTime + offset |
2026-07-09T14:32:11Z / -05:00 |
| Link | bizTransactionList |
desadv → SHIP-8837462 |
The diagram is the platform in one glance: milestone events from OMS/WMS, TMS/telematics, and carrier EDI/API (badge 1, the EDI 214) land on an append-only bus, normalise to the EPCIS model (badge 2), and build a de-duplicated shipment timeline (badge 3); from that single timeline the platform files customs pre-arrival (badge 5) and screens parties (badge 4) on one branch, and serves the customer portal, track API, and proactive ETA alerts (badge 6) on the other. A concrete EPCIS 2.0 capture event — the JSON the normaliser emits and the timeline store persists — makes the model tangible:
{
"type": "ObjectEvent",
"eventTime": "2026-07-09T14:32:11.000Z",
"eventTimeZoneOffset": "-05:00",
"epcList": ["urn:epc:id:sscc:0614141.1234567890"],
"action": "OBSERVE",
"bizStep": "urn:epcglobal:cbv:bizstep:departing",
"disposition": "urn:epcglobal:cbv:disp:in_transit",
"readPoint": { "id": "urn:epc:id:sgln:0614141.00777.0" },
"bizLocation": { "id": "urn:epc:id:sgln:0614141.00888.0" },
"bizTransactionList": [
{ "type": "urn:epcglobal:cbv:btt:desadv",
"bizTransaction": "urn:epc:id:gsrn:0614141.SHIP-8837462" }
]
}
Behind that clean model sits a messy reality: the same physical milestone often arrives from two systems at once, out of order, minutes or hours apart. The milestone-event table is the platform’s canonical vocabulary — each customer-visible milestone, the EDI or status code that signals it, the source that emits it, the EPCIS bizStep it normalises to, and whether the customer sees it — and it is what lets the milestone engine dedupe on (shipmentId, bizStep, eventTime) and show each hop exactly once.
| Milestone | Code | Source system | EPCIS bizStep | Customer-visible |
|---|---|---|---|---|
| Order booked | — | OMS | commissioning |
Yes — “Order received” |
| Picked & packed | ASN 856 | WMS | packing |
No |
| Tendered to carrier | 204/990 | TMS | staging_outbound |
No |
| Picked up | 214 AF |
Carrier | departing |
Yes — “Collected” |
| In transit / gate events | 214 X6 |
Carrier / telematics | transporting |
Yes — “In transit” |
| Customs cleared | ICS2/ACE resp. | Customs platform | inspecting |
Yes — “Cleared customs” |
| Out for delivery | 214 OA |
Last-mile | departing |
Yes — “Out for delivery” |
| Delivered (POD) | 214 D1 |
Last-mile ePOD | receiving |
Yes — “Delivered” |
| Exception | 214 SD/AG |
Any | holding |
Yes — reason shown |
The EDI 214 deserves a specific note because it is the heartbeat of the whole timeline. A 214 is the X12 Transportation Carrier Shipment Status Message; its AT7 segment carries the status and the reason code, and the MS1/MS2 segments carry the geography and equipment. A single delivery milestone looks like this on the wire:
ST*214*0001~
B10*4938*8837462*SCAC~ # ref, shipment id, carrier SCAC
LX*1~
AT7*D1**NS**20260709*1432*LT~ # D1 = delivered/completed, at local time
MS1*NEWARK*NJ*US~ # event location
MS2*SCAC*TRLR-4471~ # equipment / trailer
SE*7*0001~
The track-and-trace platform is only as good as the feeds it fuses, and each feed has its own cadence, transport, and dedup key. The source table closes the section: it is the reconciliation map the platform runs, and it is why a shipment that goes quiet on one feed — a carrier that stops sending 214s — can be kept alive by inferring the hop from a telematics geofence crossing rather than simply going dark on the customer’s screen.
| Source | Events it emits | Transport | Dedup / correlation key |
|---|---|---|---|
| OMS | Order booked, amended, cancelled | Event Hubs (native) | orderId → shipmentId |
| WMS / 3PL | Pick, pack, ASN, load | Event Hubs / EDI 856 | SSCC + orderId |
| TMS | Tender, dispatch, plan change | Service Bus | loadId → shipmentId |
| Telematics | GPS, geofence, dwell, cold-chain | IoT → Event Hubs | deviceId + shipmentId |
| Carrier | 214 status milestones | EDI (AS2) / status API | proNumber + SCAC |
| Customs | Filing accepted / cleared / held | B2G connector | entryId → shipmentId |
End-to-end order-to-delivery workflow
The surest way to see whether all of this actually holds together is to follow one shipment the whole way — from the customer’s click to the reconciled invoice — and to name, at every hop, the system that owns it, the cloud it runs on, the event it emits, the way it can fail, and the control that makes the hand-off safe. That trace is the subject of this section. The shipment is SHIP-8837462: a two-pallet, temperature-controlled load booked in the customer portal, picked at a European 3PL warehouse, line-hauled by a contracted carrier, cleared through customs, delivered on the last mile, and billed. Nothing about the journey is special — which is exactly why it is a good test of the platform.
The diagram is the trace at a glance: the order enters through the API guarded by an Idempotency-Key (badge 1), the WMS pack emits an SSCC milestone (badge 2), line-haul reports EDI 214 heartbeats (badge 3), the last-mile POD closes exactly once (badge 4), billing settles against the recorded milestones (badge 5), and a zero-downtime release of any consumer replays cleanly from the last committed offset (badge 6). Every hop is joined to the next by an event, and every event in this estate is at-least-once: the bus, the queues, and the carrier feeds can all deliver the same message twice or out of order, so correctness cannot depend on a message arriving exactly once — it has to depend on each consumer being idempotent. The per-hop table below is the spine of the workflow. Read a row as: the step, the system that owns it, the cloud it runs on, the event it publishes, the failure mode that hop is most prone to, and the control that neutralises the failure.
| # | Step | System | Cloud | Event emitted | Failure mode | Control |
|---|---|---|---|---|---|---|
| 1 | Order capture | Order API / portal | Azure APIM + AKS | order.placed |
Double-submit on retry | Idempotency-Key → one shipmentId |
| 2 | Allocate & promise | OMS | Azure AKS | order.allocated |
Oversell / no stock | ATP check, reserve then confirm |
| 3 | Pick & pack | WMS / 3PL | Azure (3PL) + EDI 856 | shipment.packed (SSCC) |
Pack without SSCC | Reject to quarantine topic |
| 4 | Tender | TMS + Logic Apps | Azure Logic Apps | EDI 204 → 990 | No 990 (limbo) | Auto-retender next carrier |
| 5 | Pickup | Carrier | Carrier + EDI | 214 AF |
No status message | Infer from geofence crossing |
| 6 | Line-haul | Telematics | AWS IoT Core / Azure IoT | 214 X6 + telemetry |
Coverage gap | Store-and-forward, replay |
| 7 | Customs | Customs platform | Azure Logic Apps (B2G) | customs.cleared |
Rejected filing | Correct HS/party, refile |
| 8 | Last-mile | Last-mile app | Azure App Service | 214 OA |
Mis-scan / wrong stop | Geo + barcode validation |
| 9 | POD | ePOD | Azure Cosmos DB | shipment.delivered |
Duplicate POD on resync | Exactly-once conditional write |
| 10 | Bill & settle | Billing + SAP | Azure SQL + SAP | freight.rated |
Under/over-bill | Rate from milestone set, reconcile |
The two controls that make this workflow survivable are idempotency and replay, and they are engineered per event rather than hoped for globally. Each event names a business key, a store that records “this effect already happened”, and a source it can be replayed from if a consumer was down or a region was lost. Order capture dedupes on a client Idempotency-Key; POD dedupes on the shipment plus the delivered step; billing dedupes on the shipment plus the invoice period. The exactly-once effect — not exactly-once delivery, which is a fiction — is a conditional write on the business key, so a duplicate is a no-op:
# Exactly-once POD effect — conditional write on the business key
Item : pk = "shipment#8837462" sk = "event#delivered"
Condition : attribute_not_exists(pk AND sk) # duplicate POD → no-op
TTL : eventTime + 30d
Effect : first write triggers billing; every replay is swallowed
| Event | Idempotency key | Dedup store | Replay source | Window |
|---|---|---|---|---|
order.placed |
client Idempotency-Key | Cosmos DB (key→result) | APIM retry / Event Hubs | 24 h |
shipment.packed |
SSCC + orderId | WMS / bus offset | Event Hubs (retained) | 7 days |
| 214 milestones | proNumber + status + time | Milestone store | Carrier redelivery / archive | 7 days |
customs.cleared |
entryId | Filing store | B2G re-poll | Until cleared |
shipment.delivered |
shipmentId#delivered | Cosmos DB (conditional) | Device store-and-forward | 30 days |
freight.rated |
shipmentId#period | Billing ledger | Event replay | Billing cycle |
Security travels with the shipment hop by hop, because “identity is the perimeter” has to mean something at every seam, not just at the front door. Each hop authenticates with a workload identity — a managed identity or IAM role, never a shared secret — reaches data over a private path (Private Endpoint or VPC endpoint, never the public internet), and applies the data control the class demands: the cold-chain and PII fields on this shipment are Restricted, so they stay in-region and are encrypted with a customer-managed key. The per-hop security table makes the seam-by-seam posture explicit.
| Hop | Workload identity | Data control | Network path |
|---|---|---|---|
| Order API | APIM + Entra-issued token | PII encrypted, tokenised card | WAF → private AKS |
| OMS / WMS | Managed identity | Restricted stays in-region | Private Endpoint to SQL |
| Tender (EDI) | Logic Apps managed identity | Signed AS2, partner-scoped | VNet-integrated, no public |
| Telematics | Device X.509 (mTLS) | Per-device identity, twin | IoT lane, segmented |
| Customs (B2G) | Connector cert | Filing data least-privilege | Governed egress, logged |
| POD / billing | Managed identity | CMK encryption, immutable | Private Link to Cosmos/SQL |
Observability is the other constant, and Dynatrace is the single pane that follows SHIP-8837462 as one distributed trace across ten systems and two clouds — from the portal PurePath, through APIM, OMS, the bus, the carrier webhook, the customs connector, and into billing — correlating the business shipmentId with the technical trace so an operator can answer “where is this shipment, and is any hop unhealthy?” from one place. Real-user and synthetic monitoring watch the customer journey; Davis AI baselines the milestone cadence and flags a shipment that has gone quiet against its own history. The signals table is what the on-call actually watches.
| Signal (Dynatrace) | What it catches | Threshold / alert |
|---|---|---|
End-to-end trace on shipmentId |
A hop that failed or stalled | Any span error / gap > SLA |
| Consumer lag per topic | A backed-up or dead consumer | Lag rising or > N minutes |
| Milestone cadence (Davis AI) | A shipment gone quiet | Missing expected 214 in window |
| DLQ depth per integration | A failing connector (carrier, customs) | DLQ > 0 (page) |
| Duplicate-effect rate | Broken idempotency | Dup effect > 0 |
| Portal RUM / synthetic | Customer-facing degradation | p95 > 300 ms, journey fail |
The last property the workflow has to prove is that it survives its own change: a zero-downtime release of any consumer must not lose, duplicate, or reorder a shipment’s events. Because every hop is already idempotent and replayable, a release is a controlled, observable event rather than a risk — the deploy drains in-flight work, runs old and new consumers side by side on the same topic, and a rollback simply re-processes from the last committed offset. The table states, for each concern, the mechanism that handles it and — the column that matters to the business — what the in-flight shipment actually experiences.
| Concern | Mechanism | What the shipment sees |
|---|---|---|
| In-flight events during deploy | Drain, then blue-green the consumer group | No loss — events buffered on the bus |
| Old + new consumer together | Idempotent handlers, versioned schema | No duplication — dedup on business key |
| Schema change | Backward-compatible, additive fields | No break — new fields ignored by old |
| Bad release | Roll back, replay from committed offset | No gap — reprocessed exactly once in effect |
| DB migration | Expand-migrate-contract, online | No downtime — reads/writes continue |
| Region loss mid-flight | Fail over, replay from retained offsets | Continues — RPO 15 min, timeline rebuilt |
Traced end to end, SHIP-8837462 is the whole architecture in miniature: ten systems across two clouds and four data centres, joined not by brittle point-to-point calls but by idempotent, replayable events; screened, filed, and cleared before it moves; visible to the customer as one clean timeline; billed against the milestones it actually generated; and carried safely through both a coverage gap on a mountain road and a Tuesday-afternoon deployment of the billing service. That is what “zero-downtime” means when it is made concrete — not a slogan on a slide, but a single parcel that never notices the machinery working underneath it.
Multi-layer security model
Zero trust is not a single product; it is seven layers that each assume the layer outside them may already be compromised. Identity (Okta, Entra, MFA, PIM, Conditional Access, SCIM) is layer one. Device (Intune, CrowdStrike Falcon, encryption, EDR) is layer two. Network (segmented hubs, firewalls, private endpoints, restricted peering, inspection) is layer three. Application (secure SDLC, Wiz Code, SAST, dependency scanning, secrets management, WAF, API protection) is layer four. Workload (hardened images, vulnerability management, image signing, runtime detection) is layer five. Data (encryption, classification, tokenisation, DLP) is layer six. Monitoring (Dynatrace, cloud logs, SIEM, ServiceNow) is layer seven. Spanning all of them is Wiz for cloud security posture and exposure management.
The reason to draw it as layers is operational: each layer has an owner, a tool, and a measurable control, and a gap in one is caught by the next. If a credential is phished (layer one), device compliance and runtime detection (layers two and five) still stand in the way; if a workload is exploited (layer five), network segmentation and data encryption (layers three and six) limit what the attacker can reach and read. Posture management ties the picture together by continuously checking that the controls are actually present and configured, not merely documented.
Multi-layer network security
The network expresses the same defence-in-depth as a path through six controls. Layer one is the third-party DNS/CDN/WAF for public ingress. Layer two is the Azure Application Gateway WAF and AWS regional ingress that protect origins and route at the app level. Layer three is Azure Firewall Premium and AWS Network Firewall behind a centralised inspection VPC/VNet, enforcing policy-controlled egress, optional TLS inspection, threat intelligence, and east-west rules. Layer four is the workhorse — NSGs, security groups, route tables, and subnet segmentation implementing a default-deny posture between application environments. Layer five is private connectivity to PaaS through Private Endpoints, Private Link, and VPC endpoints with private DNS. Layer six is the host and workload itself, hardened and running with least-privilege identity.
The same six layers look different inside each cloud, so each earns its own deep dive. In Azure, ingress lands on Application Gateway WAF v2, egress and east-west are forced through Azure Firewall Premium (with IDPS and TLS inspection) by user-defined routes, NSGs and Application Security Groups segment the spoke subnets default-deny, and PaaS is reachable only over Private Endpoints with public access disabled.
In AWS, ingress is an ALB behind AWS WAF, all east-west and egress traffic is steered through a centralised AWS Network Firewall in an appliance-mode inspection VPC, security groups and network ACLs enforce default-deny, and services are reached privately through Gateway and Interface VPC endpoints — administration via Session Manager rather than open SSH.
Two rules make this design defensible rather than merely layered. First, no unrestricted east-west routing — application environments cannot talk to each other unless a rule explicitly allows it, which contains lateral movement. Second, internet egress is policy-controlled and logged, so an exfiltration attempt has to pass a firewall that is recording it. Administrative access traverses only approved secure paths (Bastion and jump services), and flow logs from every layer feed central monitoring so the network is observable as well as segmented.
Compliance, data residency and control mapping
A global carrier that handles card payments at the point of booking, personal data for consignees across the European Union, and temperature-controlled pharmaceutical shipments under cold-chain custody does not get to assert that it is secure — it has to prove it, repeatedly, to auditors who do not take diagrams on trust. Four frameworks are therefore in scope, each treated as a distinct obligation rather than one undifferentiated “compliance” bucket. ISO/IEC 27001:2022 is the umbrella information-security management system the whole estate is certified against; SOC 2 Type II evidences that the security, availability, and confidentiality controls operated effectively over a period, not merely on audit day; PCI-DSS v4.0 applies only to the payment paths, which are deliberately tokenised and network-segmented so the cardholder-data environment stays small and the rest of the estate falls out of scope; and GDPR, with country-specific data-residency and localisation rules, governs how EU personal data and other regulated records are stored, moved, and erased. The design philosophy throughout is that one control set, expressed as policy-as-code and inherited by every landing zone, satisfies several frameworks at once — so the carrier maintains a single estate of controls and maps it outward to ISO, NIST, and CIS rather than running a separate stack per certification.
The pivot that makes residency tractable is a four-level data classification baked into the platform: Public, Internal, Confidential, and Restricted, where Restricted covers exactly the data the regulators care about most — personally identifiable information, payment data, and pharma cold-chain custody records. Classification is not a spreadsheet exercise; it is enforced where data lives. Restricted data stays in-region by default, the medallion lakehouse governance band carries the catalog, lineage, and classification tags that pin each dataset to an allowed geography, and regulated data crosses a border only over a governed, consented path the governance band itself authorises. That single rule — residency enforced at the data layer rather than asked of every application — is what lets a business operating in 70-plus countries share data globally without quietly breaching a localisation law somewhere in the network.
The control matrix below is the spine of the evidence pack: each row is a single auditable line per control domain, restating the control this estate actually runs in the language each assessor speaks — ISO 27001:2022 Annex A, NIST CSF plus the 800-53 family, and CIS Controls v8 — alongside the team that owns it. Crucially, every control names a real tool from this estate, because an auditor asks “show me” and the answer has to be Entra, Sentinel, Wiz, or an immutable Log Archive, not an aspiration.
| Control domain | This estate’s control | ISO 27001:2022 | NIST CSF / 800-53 | CIS v8 | Owner |
|---|---|---|---|---|---|
| Identity & access | Entra ID + Okta SSO/federation, Conditional Access, PIM JIT (approval + time-box), MFA for all and phishing-resistant for privileged, SCIM leaver flow from Workday | A.5.15, A.5.16, A.5.17, A.5.18 | PR.AA / IA-2, IA-5, AC-2, AC-6 | CIS 5, 6 | Cloud Security & Identity |
| Network security | Azure Firewall Premium + AWS Network Firewall behind centralised inspection, default-deny segmentation, Private Endpoints, no unrestricted east-west | A.8.20, A.8.21, A.8.22 | PR.IR / SC-7, AC-4 | CIS 4, 12 | Network & Connectivity |
| Data protection | Encryption in transit and at rest (Key Vault HSM / KMS), four-level classification, payment-path tokenisation, DLP | A.8.10, A.8.11, A.8.12, A.8.24 | PR.DS / SC-28, SC-13 | CIS 3 | Cloud Security & Identity |
| Logging & monitoring | Microsoft Sentinel SIEM, Azure Monitor / Log Analytics + CloudTrail / CloudWatch, immutable Log Archive (S3 Object Lock) | A.8.15, A.8.16 | DE.CM, DE.AE / AU-2, AU-6, AU-9 | CIS 8 | Cloud Operations |
| Vulnerability management | Wiz exposure scanning, Defender for Cloud + AWS GuardDuty, patch SLA (critical 7 days / high 30 days) | A.8.8 | ID.RA / RA-5, SI-2 | CIS 7 | Cloud Platform Engineering |
| Backup & disaster recovery | Geo-redundant and cross-cloud backup, recovery-tier RTO/RPO model, immutable restore points, rehearsed failover | A.8.13, A.8.14 | RC.RP / CP-9, CP-10 | CIS 11 | Cloud Operations |
| Change management | Mandatory PR review, policy-as-code gates (tfsec / Checkov), CAB approval tied to ServiceNow change | A.8.32 | PR.PS / CM-3, CM-4 | CIS 4 | Cloud Platform Engineering |
| Endpoint security | Microsoft Intune compliance, CrowdStrike Falcon EDR, full-disk encryption, device trust gating Conditional Access | A.8.1, A.8.7 | PR.PS / SI-3, SI-4 | CIS 1, 2 | Cloud Security & Identity |
| Cloud posture | Wiz CSPM with posture score ≥ 85 enforced as an onboarding gate, drift and exposure detection across both clouds | A.5.36, A.8.9 | ID.IM, PR.PS / CA-2, CM-6 | CIS 7 | Cloud Security & Identity |
| Software supply chain | Wiz Code (SAST / SCA / IaC scanning), governed internal artifact repositories, container scanning and image signing | A.8.28, A.8.30, A.8.31 | PR.PS / SA-11, SA-12, SR-3 | CIS 16 | Cloud Platform Engineering |
| Application security | Secure SDLC, App Gateway WAF v2 + AWS WAF, third-party edge WAF (OWASP, bot, API schema), runtime-injected secrets | A.8.25, A.8.26, A.8.27 | PR.PS / SC-7, SA-15 | CIS 16 | Application Enablement |
| Compliance & audit | ISMS governance, SOC 2 Type II evidence collection, PCI-DSS scope management on segmented payment paths, ServiceNow CMDB system of record | A.5.31, A.5.34, A.5.35 | GV.OC, ID.GV / CA-7, PM-9 | CIS 17, 18 | Cloud Center of Excellence |
Two patterns in that matrix do the heavy lifting. First, identity and posture are the domains where a single control answers to every framework at once — Conditional Access plus PIM is simultaneously ISO A.5.15–A.5.18, NIST PR.AA, and CIS 5/6, and a Wiz posture score above threshold is at once an ISO A.5.36 expectation, a NIST ID.IM signal, and a CIS 7 measurement — so the carrier earns disproportionate audit coverage from getting those two right. Second, the immutable Log Archive with Object Lock is the keystone of the evidence chain: it satisfies ISO A.8.15, NIST AU-9, and the SOC 2 criteria for tamper-evident audit trails with the same artefact, and it is the reason a ransomware actor cannot erase the very logs that would prove what they did.
Residency then gets its own table, because the rule differs by data class and so does the enforcement point: Public material can sit on the global edge, while Restricted records cannot leave their geography without an explicit, governed crossing. The table makes the boundary precise.
| Data class | Examples | Residency rule | Enforcement |
|---|---|---|---|
| Public | Marketing pages, public tracking status, published rate cards | No restriction; served globally from the edge CDN | Third-party CDN/WAF; no regulated content permitted at this tier |
| Internal | Operational dashboards, internal knowledge, non-sensitive telemetry | Regional preference; cross-region replication allowed for resilience | Lakehouse governance band tags; storage account / S3 region policy |
| Confidential | Commercial contracts, partner agreements, pricing models, business analytics | Stored in approved business regions; access on least-privilege and need-to-know | RBAC + Conditional Access; encryption with Key Vault HSM / KMS; DLP |
| Restricted — personal data (GDPR) | Consignee PII, employee records, contact and address data | Stays in-region (EU data in EU); cross-border only over governed, consented paths | Governance-band classification + residency rules; Private Endpoints; lineage and consent tracking |
| Restricted — payment & cold-chain | Cardholder data, payment tokens, pharma temperature and chain-of-custody records | Payment data tokenised and confined to the segmented PCI scope; cold-chain records pinned in-region | Tokenisation + network segmentation (PCI-DSS v4.0); Object Lock retention; in-region storage only |
The thread tying the section together is that compliance here is a property of the platform, not a quarterly scramble. Because classification, residency, encryption, logging, and posture are all enforced by inherited policy and named tooling — and because every one maps cleanly to ISO, NIST, and CIS through the matrix above — the carrier can hand an auditor a single coherent control estate and demonstrate, with evidence rather than assertion, that a parcel’s payment, a consignee’s personal data, and a pharma shipment’s cold-chain record are each governed exactly as the regulator demands.
Application onboarding for 100+ applications
A foundation is only as good as the rate at which workloads can land on it safely. The onboarding model turns 100-plus applications into a repeatable pipeline rather than 100 bespoke projects. A request is raised in ServiceNow; an automated vend (Azure subscription factory or AWS Account Factory) creates the environment under the correct management group or OU with policy, RBAC, tags, and hub or Transit Gateway attachment; a baseline is applied at creation — logging, monitoring, Wiz and CrowdStrike enrolment, backup, private DNS linking, and CI/CD bootstrap. A blueprint per application type (web, API, integration, SAP-adjacent, data, batch) supplies the right network and platform pattern, always with production and non-production separation.
The vend itself is cloud-specific but symmetrical. In Azure, a subscription is created under the right management group so it inherits Azure Policy and RBAC, then wired with diagnostic settings, Defender for Cloud, Wiz and CrowdStrike enrolment, a Recovery Services backup, hub peering, and Private DNS links.
In AWS, Account Factory for Terraform provisions the account into the right OU under its SCP guardrails, then applies CloudTrail and Config logging, GuardDuty, Security Hub, Wiz and CrowdStrike enrolment, AWS Backup, a Transit Gateway attachment, and IAM Identity Center permission sets.
Crucially, nothing reaches production until it passes a fixed set of gates: a completed threat model, a passed security baseline, a Wiz posture score above threshold, an active Dynatrace observability baseline, a DR classification, a tested backup and restore, and a registered ServiceNow configuration item with a runbook. Because the blueprint, the gates, and the pipeline templates are all reusable, the platform team improves the standard once and propagates it to every team through controlled version upgrades — agility for application teams, governance for the platform.
SAP landing zone
SAP is treated as a business-critical shared domain with its own landing zone, not as just another application. Production runs across two availability zones: clustered ASCS/ERS application servers and a SAP HANA database replicated synchronously between zones with HANA System Replication, on high-memory certified compute. QA, non-production, and sandbox environments are separated from production with their own change windows. A paired DR region holds a warm-standby HANA replicated asynchronously, with a documented and rehearsed failover path. Connectivity to on-premises systems and business partners runs privately over the same ExpressRoute and Direct Connect circuits the rest of the estate uses.
The integration surface is as important as the runtime. SAP connects to on-premises identity and legacy services, to the customer-facing logistics platforms and their APIs, to the data platform for reporting and analytics, and to ServiceNow for CMDB and change. Each of those is a private, governed path with its own privileged-access controls, because SAP sits on the critical-data tier and earns the strongest authentication and segmentation in the estate. Dedicated backup, monitoring through Dynatrace, and a separate change cadence keep SAP’s stringent availability and performance requirements from being diluted by general-purpose platform operations.
Data and integration platform
End-to-end visibility, shipment and fleet tracking, partner integration, and sustainability reporting all depend on treating data as a platform capability rather than something each application hoards locally. Sources — SAP, the 100-plus applications, IoT telemetry, and partner feeds — flow into an event-driven integration backbone (Azure Event Hubs, Kafka, or AWS Kinesis) carrying booking, tracking, telemetry, and exception events. A medallion lakehouse organises the data into raw, curated, and governed zones separated by domain, with a master-data band for customers, assets, routes, depots, and partners. Serving spans analytics and BI, sustainability and fleet-efficiency datasets, and secure partner API exposure through the edge WAF and API management.
The governance band — catalog, lineage, classification, and regional data-residency controls — is what lets a global business share data without breaking country rules. Event-driven integration decouples producers from consumers, so a new analytics use case or a new partner feed can subscribe to existing event streams without touching the source systems. That decoupling is the difference between a data platform that scales with the business and a tangle of point-to-point integrations that calcifies it.
Zero-downtime release patterns
The carrier’s operating reality is that nothing shares a maintenance window. A sortation centre runs three shifts and never goes dark; a WMS handheld in a distribution centre holds a live pick wave; a driver mid-route carries a manifest that the TMS is still tendering against; a customs broker is clearing a container before a berth deadline; and the customer portal is being refreshed by a shipper in a timezone where it is the middle of the working day. There is no 02:00 on a Sunday that is quiet everywhere. So “zero-downtime” is not an aspiration bolted onto the release process — it is the release process, and it rests on one discipline: a deploy (code reaches a server) is decoupled from a release (customers see the change). Deployment becomes boring, reversible, and continuous; the business decision to expose a change becomes a controlled, instantly reversible flip. During the November-to-January peak the change-advisory board freezes risky change, but flag-gated and canary-gated releases continue precisely because their blast radius is bounded and their rollback is pre-wired.
Every Tier-1 logistics service is mapped to the release pattern that fits its shape and its runtime, and each pattern respects a specific “cannot-stop” constraint. The portal and the public tracking API tolerate an atomic slot swap; the OMS microservices on AKS want a metric-gated canary; the TMS API on ECS uses managed blue-green with CodeDeploy; the WMS — which is closest to physical work and hardest to interrupt — leans on feature flags and a quorum-safe rolling update so a DC never loses its control plane mid-wave.
| Pattern | Logistics service | Platform mechanism | “Cannot-stop” constraint it respects | Rollback path |
|---|---|---|---|---|
| Blue-green (slot swap) | Customer portal, public track-and-trace API | Azure App Service deployment slots (staging → production swap) |
Shippers hit the portal 24/7 across all zones — no drain window exists | Swap back (VIP re-points to old slot in seconds) |
| Blue-green (managed) | TMS API (rating, tendering) | ECS + CodeDeploy blue/green, dual ALB target groups | Carriers tender against live loads; a broken deploy strands freight | CodeDeploy alarm-triggered auto-rollback to blue |
| Canary (analysis-gated) | OMS microservices (order capture, quote) | AKS + Argo Rollouts, weighted traffic + AnalysisRun |
Order intake must never drop; prove new code on 5% first | Argo abort → weight returns to stable automatically |
| Rolling (quorum-safe) | WMS services, label/print, EDI gateway | Kubernetes rolling update + PodDisruptionBudget | A DC must keep a serving quorum through the whole roll | Roll forward to previous tag; PDB blocks over-eviction |
| Feature flags (dark launch) | WMS, last-mile dispatch, portal features | Azure App Configuration feature manager / LaunchDarkly | Turn a feature on per-DC/per-region, kill instantly | Flag → off (no redeploy; effect is immediate) |
| Expand-contract (DB) | OMS order schema, TMS shipment schema | Additive migrations + views; contract weeks later | Two app versions run on one schema during rollout | Stop at expand; destructive step never shipped yet |
| Health-based traffic shift | Cross-region + cross-cloud origins | Front Door / Traffic Manager / Route 53 weighted + probes | A degraded origin must bleed traffic, not fail closed | Shift weight back / DNS priority flip |
Blue-green for the portal and tracking API — App Service slot swap
The customer portal and the public track-and-trace endpoint are stateless read-mostly front ends, which makes them the textbook case for blue-green via deployment slots. The new build is deployed to a staging slot, warmed on its readiness path, then swapped into production in a single atomic operation — the front-end VIP re-points to an already-hot set of workers, so there is no cold start and no dropped connection. Region-specific and secret settings are marked slot-sticky so they stay put and never travel across the swap.
# Track-and-trace portal — blue-green via App Service slots (West Europe, Tier-1)
az webapp deployment slot create -g rg-track-weu -n app-track-weu --slot staging
az webapp deploy -g rg-track-weu -n app-track-weu --slot staging --type zip --src-path track-v43.zip
# Warm the slot on the real readiness probe BEFORE it can take traffic
az webapp config appsettings set -g rg-track-weu -n app-track-weu --slot staging --settings \
WEBSITE_SWAP_WARMUP_PING_PATH=/health/ready \
WEBSITE_SWAP_WARMUP_PING_STATUSES=200
# Keep region/DB/secret settings pinned to their slot — they must NOT swap
az webapp config appsettings set -g rg-track-weu -n app-track-weu --slot-settings \
REGION=westeurope SQL_CONN="@Microsoft.KeyVault(SecretUri=...)"
# Atomic swap — a failed warm-up aborts the swap; production never sees a bad worker
az webapp deployment slot swap -g rg-track-weu -n app-track-weu --slot staging --target-slot production
| Slot-swap control | Setting / command | Effect |
|---|---|---|
| Warm-up path | WEBSITE_SWAP_WARMUP_PING_PATH=/health/ready |
Swap waits until the new slot returns 200 on the real readiness check |
| Warm-up gate | WEBSITE_SWAP_WARMUP_PING_STATUSES=200 |
Any non-200 aborts the swap — production stays on the good slot |
| Sticky config | --slot-settings on region/secret keys |
Connection strings and region do not travel in the swap |
| Instant reversal | swap ... --action swap back |
VIP re-points to the previous slot in seconds (MTTR ≈ swap time) |
| Pre-swap validation | swap ... --action preview (multi-phase) |
Applies production config to staging so you test the exact prod wiring first |
Canary for OMS — AKS with Argo Rollouts
Order capture is the service that must never silently regress, so the OMS microservices on AKS release as an analysis-gated canary. Argo Rollouts holds a stable and a canary ReplicaSet behind the ingress, shifts a small weight of live traffic to the canary, and — this is the point — makes the promotion decision from telemetry, not from a human watching a dashboard. An AnalysisRun queries Dynatrace (or Prometheus) every minute for the canary’s p95 latency and 5xx ratio; a breach aborts the rollout and Argo returns 100% of traffic to stable on its own.
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata: { name: oms-order-api, namespace: oms }
spec:
replicas: 12
strategy:
canary:
canaryService: oms-order-api-canary
stableService: oms-order-api-stable
trafficRouting: { nginx: { stableIngress: oms-order-api } }
steps:
- setWeight: 5
- pause: { duration: 10m }
- analysis: { templates: [{ templateName: oms-slo }] } # p95 + 5xx gate
- setWeight: 25
- pause: { duration: 30m }
- setWeight: 50
- pause: {} # hard manual gate before 100% during peak freeze
| Canary step | Traffic weight | Pause / gate | Abort criterion (auto → return to stable) |
|---|---|---|---|
| 1 — smoke | 5% | 10 min bake | canary 5xx ratio > 0.5% or p95 > 300 ms |
| 2 — analyse | 5% | AnalysisRun (Dynatrace) |
any metric query fails its threshold |
| 3 — ramp | 25% | 30 min bake | error-budget burn-rate alarm fires |
| 4 — half | 50% | manual gate (peak season) | on-call declines promotion |
| 5 — full | 100% | promote, retire old RS | — |
Blue-green for the TMS API — ECS with CodeDeploy
The TMS rating-and-tendering API runs on ECS Fargate and releases with CodeDeploy blue/green, which stands up a green task set behind a test listener, lets you validate it privately, then shifts the production listener from blue to green on a linear or canary schedule with CloudWatch alarms wired to auto-rollback. The appspec.yaml binds the new task definition to the load-balancer container/port, and a rollback is a listener flip back to the still-running blue task set — no rebuild, MTTR in seconds.
# appspec.yaml — ECS blue/green for the TMS rating API
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: <arn:...:task-definition/tms-rating:47> # green
LoadBalancerInfo: { ContainerName: tms-rating, ContainerPort: 8080 }
# Canary 10% for 5 min, then all — auto-rollback on the p95/5xx alarms
aws deploy create-deployment --application-name tms-rating \
--deployment-group-name tms-rating-bg \
--deployment-config-name CodeDeployDefault.ECSCanary10Percent5Minutes \
--auto-rollback-configuration enabled=true,events=DEPLOYMENT_FAILURE,DEPLOYMENT_STOP_ON_ALARM
| CodeDeploy element | Value | Purpose |
|---|---|---|
| Deployment config | CodeDeployDefault.ECSCanary10Percent5Minutes |
Shift 10% to green, bake 5 min, then the rest |
| Test listener | separate ALB listener → green target group | Validate green privately before the prod listener flips |
| Alarm rollback | events=DEPLOYMENT_STOP_ON_ALARM on p95/5xx |
CloudWatch alarm during shift reverts to blue automatically |
| Termination wait | BlueGreenDeploymentConfiguration terminationWaitTimeInMinutes |
Keep blue tasks alive N min so rollback is instant |
| Traffic reroute | deploymentReadyOption |
Auto or manual gate before production traffic moves |
Feature flags and quorum-safe rolling for the WMS
The WMS is the hardest service to interrupt because it is bolted to physical work: a rolling wave of picks, a conveyor sorter, a label printer. It cannot tolerate an atomic swap that momentarily quiesces connections, so its release model is feature flags plus a quorum-safe rolling update. New behaviour ships dark behind an App Configuration flag and is switched on one DC at a time; the deployment itself rolls pod-by-pod under a PodDisruptionBudget that guarantees the DC never drops below a serving quorum mid-wave.
| Flag type | Logistics example | Lifetime | Owner |
|---|---|---|---|
| Release toggle | New OMS rating engine behind v43-rating |
Days–weeks (removed after 100% rollout) | Delivery team |
| Ops kill switch | Disable EDI partner X connector under load | Permanent | Platform on-call |
| Experiment | Alternate ETA model for last-mile, 10% of routes | Weeks (until decision) | Data science |
| Per-tenant / per-DC | Enable new putaway logic for one distribution centre | Long-lived | WMS product |
| Circuit breaker | Shed customs-lookup calls when the broker API is slow | Permanent | SRE |
Expand-contract database change — the linchpin
None of the above survives a breaking schema migration, so every stateful change follows expand-contract: the schema only ever adds in the release that ships, so the old and new app versions run against the same database simultaneously; the destructive change (dropping a column, tightening a constraint) is a separate migration weeks later, once the new version is the only one live. This is what lets a canary or a slot swap touch the OMS order table at all — at no point does a running version meet a schema it does not understand.
| Phase | Schema action | App behaviour | Reversible? |
|---|---|---|---|
| 1 · Expand | Add nullable column / new table / compatibility view | Old + new versions both read old shape | Yes — pure additive |
| 2 · Migrate | Backfill new column online, in batches | New writes populate both old and new | Yes — backfill re-runnable |
| 3 · Dual-write | (behind flag) write old + new, read new | Canary reads new; stable reads old | Yes — flag off reverts reads |
| 4 · Contract | Drop old column / constraint | Only new version remains | No — gated on 100% + soak |
Health-based traffic shift and rollback
Above the individual services, the global edge treats traffic as a dial, not a switch. Front Door, Traffic Manager, and Route 53 weight origins and probe their health, so a degraded region bleeds traffic away instead of failing closed, and a fast error-budget burn-rate alarm in Dynatrace is the single trigger that fires the correct reverse action per pattern.
| Signal | Threshold | Automated action | Typical MTTR |
|---|---|---|---|
| Slot swap warm-up fails | any non-200 on /health/ready |
Swap aborts; production untouched | 0 (never exposed) |
| Canary metric breach | 5xx > 0.5% or p95 > 300 ms | Argo abort → 100% to stable | < 60 s |
| ECS deploy alarm | CloudWatch p95/5xx alarm | CodeDeploy rollback to blue | < 60 s |
| Feature regression | flag-scoped error spike | App Config flag → off | Seconds |
| Region degradation | health probe 3× fail / burn-rate ≥ 14.4 | Edge weight → 0 / DNS priority flip | < 2 min |
Reading the diagram left to right: the global edge sends the whole portal to the warmed slot but only a thin 5% slice of the tracking API to the canary; both versions share one expand-contract database and a feature-flagged read path; and a Dynatrace SLO burn-rate alarm is wired to the pattern-specific rollback so recovery is measured in seconds because the previous good version was never torn down.
Active-active multi-region data topology
The hard part of running two regions live is never the compute — stateless portal and API pods scale in any region — it is the data. The design starts from a blunt classification: which data can safely take writes in more than one region, and which data has exactly one writer, ever. Track-and-trace positions, shipment scan events, portal session and catalogue state, and fleet telemetry are high-volume, commutative or last-writer-wins-tolerant facts — they go active-active. The OMS order ledger, financial postings, and the customs declaration of record are legally and numerically singular: you cannot merge two divergent versions of an invoice or a customs entry, so they are region-pinned to a single writer with read replicas elsewhere. Getting this split right is the whole game; everything downstream is mechanism.
| Data class / logistics service | Store | Write topology | Region-pinned rule |
|---|---|---|---|
| Track-and-trace positions & scans | Cosmos DB | Multi-region write | None — any region writes; conflicts auto-resolved |
| Fleet telemetry (500k devices) | DynamoDB global table | Multi-region write | None — last-writer-wins per item |
| Portal session / catalogue / rates | Cosmos DB / DynamoDB | Multi-region write | None — LWW acceptable |
| Shipment events (GS1 EPCIS) | Event Hubs / Kafka / Kinesis | Producers in both regions | Ordered per shipment key, idempotent |
| OMS order ledger | Azure SQL failover group | Single writer | Writes only to primary region; replica is read-only |
| Financial postings / SAP | Aurora Global / HANA | Single writer / primary | One primary region; async replica for DR |
| Proof-of-delivery images, customs docs | Blob RA-GRS / S3 CRR | Object replication | Immutable original in one region, replicated read copies |
The track-and-trace event pipeline is the canonical active-active consumer here: it is written from whichever region a scan or a device reports to, and every downstream reader sees a single converged model because the event backbone enforces per-shipment ordering and idempotency.
Which datastore replicates how
Each store earns its own replication mechanism, RPO, and conflict rule — there is no single “replicate the region” button, and pretending there is causes silent data loss. The table below is the reference the platform team builds against.
| Datastore | Replication mechanism | Topology | RPO | Conflict handling |
|---|---|---|---|---|
| Cosmos DB | Native multi-write, async, bounded staleness | Active-active | Seconds (sub-5s typical) | LWW on /_ts or custom merge sproc |
| DynamoDB global tables | Stream-based cross-region replication | Active-active | ≈ 1 s | Last-writer-wins on item (built-in) |
| Azure SQL failover group | Async geo-replication, one primary | Active-passive (writer) | ≈ 5 s | N/A — single writer, no conflict |
| Aurora Global Database | Storage-level cross-region replication | Active-passive (writer) | < 1 s (typ. ~1 s lag) | N/A — single writer |
| Event Hubs | Geo-replication (data) — Geo-DR replicates metadata only | Active-active producers | Seconds (Geo-repl) / config-only (Geo-DR) | Idempotent keys + per-key ordering |
| Kafka (Confluent/self-managed) | MirrorMaker 2 / Cluster Linking | Active-active | Seconds | Consumer-side dedupe on key |
| Kinesis Data Streams | App-level cross-region replicator (no native) | Active-active | Seconds | Idempotent consumers |
| Blob storage | RA-GRS / object replication | Read replica (async) | < 15 min | N/A — immutable original |
| Amazon S3 | Cross-Region Replication (+ RTC 15-min SLA) | Read replica (async) | < 15 min (RTC) | N/A — versioned, immutable |
A note that separates real designs from diagrams: Event Hubs Geo-DR (the alias/failover feature) replicates namespace configuration only — it does not copy your event data. For genuine cross-region message continuity you use the newer Event Hubs Geo-replication (premium/dedicated) or federate with a MirrorMaker-style pump; treating Geo-DR as a data-replication feature is a classic and expensive misread.
Region-pinned writers and conflict resolution
For the multi-write stores, a conflict policy is not optional — under concurrent writes the store will diverge, and the only question is whether resolution is deterministic. Cosmos is configured with an explicit conflict-resolution policy per container: last-writer-wins on a timestamp path for telemetry, or a custom stored procedure where two depots legitimately update the same shipment and a naive overwrite would lose a scan. DynamoDB global tables apply last-writer-wins automatically. The pinned stores sidestep the problem entirely by having one writer.
# Cosmos DB — multi-region write for track-and-trace, bounded staleness + LWW
resource "azurerm_cosmosdb_account" "track" {
name = "cosmos-track-global"
offer_type = "Standard"
kind = "GlobalDocumentDB"
multiple_write_locations_enabled = true # active-active writes
consistency_policy {
consistency_level = "BoundedStaleness" # bounded lag, not eventual
max_staleness_prefix = 100000
max_interval_in_seconds = 5 # ≤ 5 s replication window
}
geo_location { location = "westeurope" failover_priority = 0 }
geo_location { location = "northeurope" failover_priority = 1 }
}
resource "azurerm_cosmosdb_sql_container" "events" {
# ... partition_key_path = "/shipmentId" keeps a shipment's events co-located
conflict_resolution_policy {
mode = "LastWriterWins"
conflict_resolution_path = "/_ts" # highest timestamp wins
}
}
# DynamoDB global table — fleet telemetry, active-active, LWW built in
resource "aws_dynamodb_table" "fleet" {
name = "fleet-telemetry"
billing_mode = "PAY_PER_REQUEST"
hash_key = "deviceId"
stream_enabled = true
stream_view_type = "NEW_AND_OLD_IMAGES"
replica { region_name = "eu-west-1" }
replica { region_name = "eu-central-1" }
}
| Store | Resolution mode | Config | Logistics example |
|---|---|---|---|
| Cosmos (telemetry) | Last-writer-wins | conflict_resolution_path = /_ts |
Two GPS pings for one truck — newest wins |
| Cosmos (shipment) | Custom merge sproc | mode = "Custom" + JS procedure |
Two depots scan same parcel — union the scans |
| DynamoDB global | Last-writer-wins | built-in (per-item, replica timestamp) | Device state update from two gateways |
| SQL / Aurora | None (single writer) | failover group / global cluster | OMS order total — never multi-mastered |
| Event backbone | Idempotent + ordered | key = shipmentId, dedupe window |
Replayed EPCIS event is a no-op |
Write routing and consistency knobs
Latency-based DNS pins each user to the nearest active region for reads and local writes, while the pinned-writer paths route their writes to the owner region regardless of where the user landed — the portal in Region B can read an order locally but its write to the order ledger is proxied to the primary region’s SQL listener.
| Path | Router | Rule | Failure behaviour |
|---|---|---|---|
| Portal / tracking reads | Traffic Manager (Performance) / Route 53 latency | Nearest region | Reroute to healthy region |
| Telemetry / track writes | Local region endpoint | Write local, replicate async | Buffer at edge, replay on backbone |
| OMS order writes | SQL failover-group R/W listener | Always to primary region | Blocks until primary reachable / failover |
| Object upload (POD) | Region-local bucket/container | Write local; CRR/RA-GRS replicates | Retry; original is immutable |
| Store | Consistency setting | Read latency | Write latency / cost note |
|---|---|---|---|
| Cosmos multi-write | Bounded staleness (≤ 5 s / 100k ops) | Single-digit ms local | Higher RU cost than eventual; predictable lag |
| DynamoDB global | Eventually consistent cross-region | Single-digit ms local | Replication billed as replicated write units |
| SQL failover group | Strong in-region; async to secondary | ms in primary region | Cross-region write = primary round-trip |
| Aurora Global | Strong primary; < 1 s replica lag | ms in primary | Secondary is read-only until promoted |
The diagram shows latency-based DNS pinning each shipper to the nearest active region, Cosmos and DynamoDB taking multi-region writes for track-and-trace while the OMS SQL ledger keeps a single writer, and the GS1 EPCIS event backbone carrying ordered, idempotent facts between the regions with a mandatory conflict policy and object replication underneath.
Active-passive and warm-standby topology
Active-active is the right answer only where the data model tolerates it; for the stateful writers — the OMS ledger, SAP, the TMS shipment store — the carrier runs active-passive on a warm standby, and chooses the rung on the DR ladder per tier by trading recovery speed against standing cost. The ladder runs from cheap-and-slow backup-and-restore, through pilot-light (data replicating, compute switched off), to warm-standby (a small always-on copy), to full active-active. Tier-3 workloads sit on backup-restore; Tier-2 on pilot-light; the Tier-1 stateful services on warm-standby; and only the stateless edge and read paths on active-active. The point of the ladder is that you do not pay active-active prices to protect a nightly analytics job, and you do not accept a 24-hour restore for order intake.
| Strategy | Standby footprint | RTO | RPO | Rel. monthly cost | Logistics tier / service |
|---|---|---|---|---|---|
| Backup & restore | Backups only; nothing running | Hours–day | Hours (last backup) | ~5% | Tier 3 — dev/test, non-critical reporting |
| Pilot light | Data replicating; compute off | 1–4 h | Minutes | ~15% | Tier 2 — internal apps, analytics, lakehouse |
| Warm standby | Small always-on copy + replica | 15 min–2 h | Seconds | ~35% | Tier 1 — OMS, TMS, SAP prod, partner APIs |
| Active-active | Full second region, live | Seconds (auto) | Seconds | ~100% | Tier 0/1 edge — portal, tracking, DNS, identity |
Mapping the estate onto the ladder makes the standby posture explicit for each service, and pins the exact primary-to-DR replication mechanism that sets its RPO.
| Logistics service | Tier | Strategy | Primary → DR mechanism |
|---|---|---|---|
| Customer portal, public tracking | 1 (edge) | Active-active | Multi-region Cosmos + health-checked DNS |
| OMS order ledger | 1 | Warm standby | Azure SQL failover group, async geo-replica |
| TMS shipment store | 1 | Warm standby | Aurora Global Database, < 1 s replica |
| SAP (HANA) | 1 | Warm standby | HANA System Replication, async to DR region |
| Line-of-business apps | 2 | Pilot light | Geo-replicated data; IaC re-hydrates compute |
| Data lakehouse / analytics | 2 | Pilot light | GRS storage + event-backbone replay |
| Dev / test / reporting | 3 | Backup & restore | GRS immutable backup, restore on demand |
Promotion and failover mechanics
A warm standby only earns its keep if promotion is scripted, deterministic, and one-way. Each stateful store has a defined failover command and a defined RTO, and — critically — the SAP HANA promotion is not a database button but a cluster takeover that must fence the old primary first.
# Azure SQL — auto-failover group protects the OMS ledger; RW listener follows primary
az sql failover-group create -g rg-oms -n fog-oms --server sql-oms-weu \
--partner-server sql-oms-neu --failover-policy Automatic --grace-period 1 \
--add-db oms-orders
# Unplanned, disaster promote (accepts the async-replication RPO gap):
az sql failover-group set-primary -g rg-oms -n fog-oms --server sql-oms-neu --allow-data-loss
# Aurora Global — planned switchover is RPO 0; unplanned failover accepts a few seconds
aws rds failover-global-cluster --global-cluster-identifier gc-tms \
--target-db-cluster-identifier arn:aws:rds:eu-central-1:...:cluster:tms-neu # planned (RPO 0)
aws rds failover-global-cluster --global-cluster-identifier gc-tms \
--target-db-cluster-identifier arn:aws:rds:eu-central-1:...:cluster:tms-neu --allow-data-loss # unplanned
# SAP HANA — takeover on the DR secondary AFTER the old primary is fenced (STONITH)
hdbnsutil -sr_takeover # promote secondary; cluster must guarantee old primary is stopped
| Datastore | Failover type | Command / action | RTO | One-way? |
|---|---|---|---|---|
| Azure SQL failover group | Auto (grace-bounded) or forced | set-primary --allow-data-loss |
Seconds–1 h grace | Yes — old = new secondary |
| Aurora Global | Planned switchover / unplanned | failover-global-cluster [--allow-data-loss] |
< 1 min / seconds | Yes — re-seed old |
| Cosmos (single-write mode) | Priority-based | az cosmosdb failover-priority-change |
Seconds (auto) | Auto-failback optional (off) |
| SAP HANA | Cluster takeover | hdbnsutil -sr_takeover + fence |
Within Tier-1 2 h | Yes — manual failback |
| Event backbone | Alias / consumer re-point | Geo-repl failover / re-point group | Seconds | Re-pair after |
Split-brain avoidance and failover testing
The failure that turns a DR event into a data-loss event is split-brain: two regions each believing they are primary and both accepting writes to the ledger, producing two divergent truths that cannot be merged. The design forbids it structurally — there is exactly one writer at all times, the old primary is fenced before the new one is promoted, and failback is always a planned, scheduled event rather than an automatic race against replication.
| Risk | Control | Mechanism |
|---|---|---|
| Two writers accept ledger writes | Single-writer invariant | Failover promotes exactly one; old primary demoted/fenced |
| Old primary returns and writes | Fencing (STONITH) | Stop/isolate old node before promote; SQL FoG guarantees single R/W listener |
| DNS flaps both regions live | TTL + probe hysteresis | 30 s TTL, 3-failure threshold — a blip does not flip |
| Auto-failback corrupts data | Manual, scheduled failback | No auto-failback on stateful writers; re-seed then cut over in a window |
| Replicated corruption / ransomware | Immutable backup floor | GRS + Object Lock; point-in-time restore is a separate control |
Finally, an RTO that has never been rehearsed is a guess, so the recovery paths are exercised on a calendar rather than in an emergency. The cadence mirrors the estate’s DR programme: a rotating quarterly per-tier component test against the paired region, and an annual full-region game-day in which a whole region is declared lost and dependent workloads fail over in strict tier order (0 → 1 → 2 → 3), with business sign-off before a controlled failback.
| Cadence | Scope | What’s rehearsed | Pass criterion |
|---|---|---|---|
| Quarterly | One tier’s recovery path (rotating) | SQL/Aurora promote, HANA takeover, DNS cutover | Meets tier RTO/RPO; deviations logged as ServiceNow change |
| Quarterly | Zero-downtime rollback | Slot swap-back, Argo abort, flag kill | Recovery in target MTTR, no customer-visible error |
| Annual | Full-region game-day | Region loss, ordered tier failover, split-brain drill | Business sign-off; failback clean; runbook corrections filed |
| Continuous | Health-checked DNS failover | Automatic origin failover under synthetic fault | Traffic shifts within TTL; no split-brain observed |
The diagram traces the active region serving OMS/TMS with SQL and HANA primaries, async replication and immutable GRS backup feeding a pilot-lit warm standby that runs one instance and a read-only replica, and a health-checked DNS layer that flips priority to the standby on failure — after which promotion is one-way and the old primary is fenced, so the ledger never has two writers.
Disaster recovery and resiliency
Resilience is designed by classifying workloads into recovery tiers and engineering each tier to its target. Tier 0 — identity, the network control plane, DNS, VPN, and security tooling — carries an RTO of one hour and an RPO of fifteen minutes because nothing else recovers until it does. Tier 1 — customer logistics platforms, SAP production, and partner APIs — matches that fifteen-minute RPO with a two-hour RTO. Tier 2 (line-of-business apps and analytics) and Tier 3 (dev, test, and non-critical reporting) relax to hours. Each strategic geography runs a primary and a paired DR region in both Azure and AWS; edge services and selected APIs run active-active behind global DNS failover, while stateful back ends use active-passive or warm standby where active-active is impractical.
| Tier | Example workloads | RTO | RPO |
|---|---|---|---|
| Tier 0 | Identity, connectivity, security control plane | 1 hour | 15 minutes |
| Tier 1 | Customer platforms, SAP prod, partner APIs | 2 hours | 15 minutes |
| Tier 2 | Internal apps, analytics, collaboration | 8 hours | 4 hours |
| Tier 3 | Dev, test, non-critical reporting | 24 hours | 24 hours |
The redundant ExpressRoute and Direct Connect circuits, cross-cloud and cross-region backup, and regular DR testing — identity recovery, DNS failover, circuit failover, and SAP recovery — turn these numbers from aspiration into something the business can actually rely on. A recovery target that has never been rehearsed is a guess; the operations model bakes the rehearsals in.
Disaster recovery runbooks
A recovery target that nobody has rehearsed is not a commitment, it is a guess. The DR tiers earlier in this document set the contractual envelope — Tier 0 at RTO 1 hour / RPO 15 minutes, Tier 1 at RTO 2 hours / RPO 15 minutes, Tier 2 at RTO 8 hours / RPO 4 hours, and Tier 3 at RTO 24 hours / RPO 24 hours — but the envelope is only credible if every scenario behind it has a written procedure with a named trigger, ordered steps, a single accountable owner, and an explicit validation that proves the workload is actually serving traffic again. The runbooks below are deliberately operational rather than aspirational: each one is the script an on-call engineer follows under pressure at 03:00, not a paragraph of intent. They presume the dual-cloud, four-region estate already in place — paired-DR regions in both Azure and AWS, asynchronous HANA System Replication to the DR region, immutable backup with Object Lock in the AWS Log Archive, and third-party DNS health-checking in front of both clouds — so recovery is a matter of promotion and re-pointing, not rebuilding.
Procedures decay unless they are exercised, so the operating model treats DR as a calendar event rather than an emergency-only capability. The carrier runs quarterly per-tier component tests — a rotating schedule in which one tier’s recovery path (identity failover, a SAP DR promotion, a data-replay, an edge origin swap) is rehearsed in isolation against the paired region, with results, timings, and any deviation from RTO/RPO logged as a ServiceNow change and fed back into the runbook. Once a year the programme runs a full-region failover game-day: an entire strategic region is declared lost and the dependent workloads are failed over in tier order — Tier 0 first to re-establish the identity and connectivity control plane, then Tier 1, and so on — with business stakeholders signing off recovery before a controlled failback. Game-day findings are the single most reliable source of runbook corrections, because they surface the dependencies and sequencing assumptions that component tests in isolation never expose.
| Scenario | Trigger | Procedure steps | Owner | Target | Validation |
|---|---|---|---|---|---|
| Identity & control-plane recovery (Tier 0) | Loss of Microsoft Entra ID / AD DS authentication or security control plane in primary region | Detect via Sentinel/Dynatrace alert → activate a monitored break-glass account → fail Entra ID and on-prem AD DS services over to the paired region → trigger third-party DNS failover for identity endpoints → confirm Conditional Access and PIM are serving → controlled failback once primary is healthy | Cloud Security & Identity | RTO 1 hour / RPO 15 minutes | Test interactive sign-in, MFA challenge and a PIM JIT elevation against the recovered control plane |
| Connectivity / circuit failover | Loss of an ExpressRoute or Direct Connect circuit in a region | Active-active BGP withdraws the failed path automatically → on-call confirms the surviving circuit is carrying the load and is not saturated → raise carrier ticket → restore and re-balance BGP on circuit return | Network & Connectivity | Sub-minute (automatic) | Synthetic reachability probe across the surviving path; confirm no asymmetric routing |
| SAP DR (Tier 1) | Loss of the SAP primary region or unrecoverable HANA primary | Declare DR → promote the asynchronously replicated HANA secondary in the paired DR region → start ASCS/ERS in DR → re-point application servers and integration to DR endpoints → run smoke tests → obtain business sign-off → controlled failback when primary is restored | Cloud Platform Engineering + SAP Basis | RTO 2 hours / RPO 15 minutes | Execute the agreed SAP transaction set (order-to-cash / tracking posting) and reconcile against last committed state |
| Edge / application failover | DNS health checks mark a region’s origin unhealthy | Third-party DNS + health checks automatically fail the affected service over from Azure origin to AWS origin (or vice versa) → on-call confirms the healthy origin is scaled and serving → monitor error budget → revert when the failed origin recovers | Network & Connectivity + Application Enablement | Automatic | Synthetic end-to-end user journey (login → track shipment) against the failed-over origin |
| Data platform recovery (Tier 2) | Loss or corruption of streaming / lakehouse storage in a region | Replay event streams from retained offsets (Event Hubs / Kafka / Kinesis) → restore the lakehouse from geo-redundant storage in the paired region → re-run curation to rebuild governed and master-data bands → resume downstream consumers | Cloud Operations | RTO 8 hours / RPO 4 hours | Record-count and checksum reconciliation between source offsets and restored curated datasets |
| Ransomware / full region loss | Confirmed destructive compromise or total region outage | Isolate the affected accounts/subscriptions → stand up (or vend) a clean landing zone → restore from immutable, Object-Lock backup into the clean estate → recover workloads in strict tier order (0 → 1 → 2 → 3) → forensics before any re-connection | Cloud Security & Identity + Cloud Operations | Per affected tier | Integrity verification of restored data against backup hashes; clean-state attestation before traffic is admitted |
Backup, immutability and ransomware recovery
Replication answers one question — the primary region is gone, where do I run? — and the resiliency and DR-runbook sections answer it with paired regions, HANA System Replication, and DNS failover. Backup answers a darker one: the data itself is wrong — corrupted by a bad release, fat-fingered by an operator, or encrypted by an intruder holding valid credentials — where do I go back to? Replication copies corruption to the secondary in milliseconds, so a warm standby that has just mirrored a ransomware payload is not a recovery, it is a second casualty. Backup is therefore a separate control plane from DR — its own vaults, retention, immutability, and, above all, its own trust boundary — so an actor who can reach production cannot reach the copies that would rebuild it.
The carrier’s data does not all carry the same recovery weight, so the backup estate is tiered by what the data means to the business, not by the technology that stores it. At the centre sits the shipment and consignment record — the system of record (SoR) for the physical network — the authoritative ledger of every parcel, pallet, and container the carrier has taken into custody; around it run the Order (OMS), Warehouse (WMS), and Transport Management Systems (TMS) that act on that record, and beneath them the SAP core that settles the money. These are the datasets a customs authority, an insurer, or a court demands years after the shipment moved, and precisely the ones a ransomware crew encrypts first. The matrix pins each class to a source engine, a backup frequency (its true backup RPO, distinct from the replication RPO the DR tiers quote), operational and long-term retention windows, and the immutable copies that make the record un-erasable.
| Data class / system | Tier | Source & engine | Backup RPO | Op. retention | Long-term | Immutable copies |
|---|---|---|---|---|---|---|
| Shipment / consignment SoR | 1 | Azure SQL BC / Aurora — PITR + export | Log 5–15 min; daily full | 35 d | 7 yr (customs) | Locked RSV + Object Lock + air-gap |
| OMS / WMS / TMS operational DBs | 1 | Azure SQL, PostgreSQL Flex, Aurora, RDS | PITR 15 min; daily | 35 d | 1 yr | Locked vault + cross-region |
| SAP HANA (production) | 1 | Backint → RSV / AWS Backup | Log 15 min; daily full | 35 d | 7 yr monthly | Locked vault + air-gap |
| Master data & lakehouse band | 2 | ADLS Gen2 / S3 versioning + snapshots | Daily | 30 d | 1 yr | Object Lock + versioning + CRR |
| Platform config & IaC state | 1 | Git + Terraform state + policy export | On change + daily | 90 d | Git history | Object-Lock backup of state |
| Secrets & keys | 0 | Key Vault soft-delete + purge / KMS | Continuous | 90-d soft-delete | Re-issue | Purge protection + multi-region KMS |
| IaaS VMs & file shares | 2 | Azure Backup / AWS Backup (EBS, EFS) | Daily | 30 d | 90 d | Locked vault |
| IoT offsets & cold telemetry | 2/3 | Event Hubs Capture / Kinesis → S3 | Continuous | 30 d | 1–7 yr | Object-Lock cold tier |
The line running through that table is the gap between backup RPO and replication RPO, and conflating the two is how an enterprise discovers mid-incident that it cannot recover. The DR tiers promise a 15-minute replication RPO for Tier-1 because HANA System Replication and geo-redundant storage keep a near-live mirror — but a mirror corrupts when the source does. Backup adds point-in-time copies you can walk backwards through: when the latest state is poisoned the carrier steps to the last log backup, then yesterday’s full, then a monthly copy inside the seven-year window the SoR earns because a mis-delivered pharmaceutical consignment or a disputed customs claim surfaces long after the operational systems have forgotten it.
Backup is expressed differently per resource type and per cloud, and getting the engine right per workload is what separates a backup policy from a backup product slide: IaaS and SAP HANA back up through a vault, PaaS databases lean on native point-in-time plus a periodic export into a WORM target, object stores rely on versioning and Object Lock.
| Workload type | Primary engine | Method | Vault / target | Notable control |
|---|---|---|---|---|
| IaaS VM (Windows / Linux) | Azure Backup (RSV) / AWS Backup | App-consistent snapshot | Locked RSV (GRS) / Backup vault | Instant-restore + long-term tier |
| PaaS relational DB (SQL, PostgreSQL, Aurora, RDS) | Native PITR + LTR; AWS Backup | Continuous log + periodic export | Geo-redundant storage + Object-Lock export | LTR to 10 yr; export = WORM |
| NoSQL (Cosmos DB, DynamoDB) | Continuous backup / PITR; AWS Backup | Point-in-time restore | Service-managed + Backup vault | PITR window + export |
| SAP HANA | Backint-certified agent | Log 15 min + daily data backup | RSV / AWS Backup | Savepoint-consistent stream |
| PaaS storage / object (Blob, ADLS, S3) | Versioning + soft delete + AWS Backup | Version retention + object copy | Object Lock (S3) / immutable Blob | WORM at the object |
| Config & IaC state | Git + pipeline export | Versioned commit + state backup | Object-Lock bucket + Git | Rebuild from code |
| Secrets & keys | Key Vault / KMS + Secrets Manager | Soft-delete + purge protection | Vault-managed; multi-region key | Recover, not into old trust |
| Kubernetes (AKS / EKS) state | Azure Backup for AKS / AWS Backup + Velero | Namespace + PV snapshot | Backup vault + Object-Lock bucket | Manifests in Git; data in vault |
Reading left to right: the data classes — IaaS and SAP HANA, the managed OMS/WMS/TMS databases, and the platform’s own config and secrets — back up into WORM-locked vaults in both clouds (1), held by a soft-delete safety net (2) and copied to an air-gapped vault in an isolated account no production credential can reach (3). Recovery never runs back into the blast radius: a validated, known-clean restore point (5) is restored into a freshly vended clean landing zone (4), and forensics preserves evidence before traffic is readmitted (6).
Immutability is the load-bearing control, because every other backup discipline is defeated the moment a privileged attacker deletes the backups before encrypting the primaries — and modern operators target the backup catalog first. The estate answers with independent controls, none trusted to hold alone: WORM immutability sets a floor beneath which nothing can be deleted or shortened; soft delete catches what immutability does not; multi-user authorisation forces destructive operations through an approver the backup admin cannot touch; and the air-gapped copy sits outside the production trust boundary.
| Control | Azure mechanism | AWS mechanism | What it blocks | Confirm |
|---|---|---|---|---|
| WORM / immutability | RSV / Backup-vault immutability = Locked | S3 Object Lock COMPLIANCE; AWS Backup Vault Lock | Delete-before-retention; retention shortening — even by root | state = Locked; Vault Lock COMPLIANCE |
| Soft delete / recycle | Backup soft delete AlwaysON, 14–180 d | AWS Backup retention + delete protection | Accidental or malicious RP deletion | softDeleteFeatureState = AlwaysON |
| Retention lock | Immutable policy floor per vault | Vault Lock min / max_retention_days |
An admin rewriting policy to 1 day | Policy diff blocked post-lock |
| Multi-user authorisation | Resource Guard in a separate tenant (PIM-gated) | SCP deny on backup:Delete*; separate account |
Solo-admin destruction of copies | Resource-guard mapping present |
| Encryption of backups | CMK in Key Vault HSM | KMS CMK per backup vault | Reading / exfiltrating backups | Vault bound to customer key |
| Destructive-op alerting | Diagnostics → Sentinel on StopProtection |
CloudTrail → SIEM on DeleteBackupVault |
A silent strip going unnoticed | Alert fires on the operation |
The Azure vault is provisioned immutable, geo-redundant, and CRR-capable in one declaration — and the ordering matters: GRS and the CRR flag must be set before any item is protected, and the Locked state is irreversible, so the platform team soaks on Unlocked first.
resource "azurerm_recovery_services_vault" "logistics" {
name = "rsv-log-weu-prod"
location = "westeurope"
resource_group_name = azurerm_resource_group.backup.name
sku = "Standard"
storage_mode_type = "GeoRedundant" # GRS — enables cross-region restore
cross_region_restore_enabled = true # must be set BEFORE protecting items
soft_delete_enabled = true # 14-day floor, extendable to 180
immutability = "Locked" # WORM — irreversible; soak on Unlocked first
identity { type = "SystemAssigned" }
}
# Prove the immutability state is the irreversible Locked, not merely Unlocked
az resource show --ids "$VAULT_ID" \
--query "properties.securitySettings.immutabilitySettings.state" -o tsv # -> Locked
On AWS, the air-gap copy lands in an S3 bucket with Object Lock in COMPLIANCE mode — a retention no principal, including account root, can shorten — while AWS Backup’s own vault is sealed with a Vault Lock whose short cooling-off window is the last chance to correct a mistake before the lock is permanent. A backup plan then does the copying, so immutability travels with the data: one rule writes the daily point into the locked vault and fans out two copy_action blocks — cross-region to the paired DR region, and cross-account into the air-gap vault.
resource "aws_s3_bucket" "backup_airgap" {
bucket = "log-backup-airgap-euc1"
object_lock_enabled = true # only settable at bucket creation
}
resource "aws_s3_bucket_object_lock_configuration" "airgap" {
bucket = aws_s3_bucket.backup_airgap.id
rule { default_retention { mode = "COMPLIANCE" # not even root deletes before expiry
days = 35 } }
}
resource "aws_backup_vault_lock_configuration" "logistics" {
backup_vault_name = aws_backup_vault.logistics.name
changeable_for_days = 3 # cooling-off; after this, compliance mode is permanent
min_retention_days = 35
max_retention_days = 2555 # 7 years for SoR / compliance copies
}
resource "aws_backup_plan" "sor" {
name = "log-sor-daily"
rule {
rule_name = "daily-35d"
target_vault_name = aws_backup_vault.logistics.name
schedule = "cron(0 2 * * ? *)"
lifecycle { delete_after = 35 }
copy_action { # cross-region copy (offsite)
destination_vault_arn = aws_backup_vault.dr_euw1.arn
lifecycle { delete_after = 35 }
}
copy_action { # cross-account copy (air-gap)
destination_vault_arn = "arn:aws:backup:eu-central-1:${var.vault_account}:backup-vault:log-airgap"
}
}
}
The whole design is a modern reading of the 3-2-1-1-0 rule, updated for a dual-cloud estate where “a second building” becomes “a second cloud and a second, un-trusted account”. The two additions to the classic 3-2-1 — the extra 1 for an offline/immutable copy, the 0 for verified-zero recovery errors — are the ones that specifically defeat ransomware and silent corruption, so the carrier treats them as non-negotiable.
| Rule | Meaning | How the estate meets it |
|---|---|---|
| 3 copies | Three copies of the data | Production + backup vault + cross-region / air-gap copy |
| 2 media / platforms | Two different platforms | Azure Backup and AWS Backup + object stores — no single vendor holds all copies |
| 1 offsite | One copy elsewhere | Cross-region copy into the paired DR region in both clouds |
| 1 offline / immutable | One air-gapped or WORM copy | Object Lock COMPLIANCE + Vault Lock in an isolated account with no standing production trust |
| 0 errors | Recoveries verified, not assumed | Automated restore drills + hash and reconciliation checks before a backup counts as “green” |
The estate is therefore explicit about where each tier’s copies live, so no single failure — a region, a cloud account, a compromised admin — takes the primary and every copy at once. Tier-0 and Tier-1 carry the full complement; lower tiers taper the far copies to control cost without breaching the floor.
| Tier | Primary vault | Cross-region copy | Cross-cloud / cross-account | Air-gap / offline |
|---|---|---|---|---|
| Tier 0 (identity, config, secrets) | Locked vault + Git, primary | Paired region | Mirrored across both clouds | Object-Lock copy, isolated account |
| Tier 1 (SoR, OMS/WMS/TMS, SAP) | Locked RSV / AWS Backup | Paired DR region | Cross-account to backup account | Object Lock COMPLIANCE, 7-yr floor |
| Tier 2 (VMs, master data, lakehouse) | Locked vault, primary | Paired DR region | Optional cross-account | Object Lock 30–90 days |
| Tier 3 (dev, test, reporting) | Vault, primary | Optional | — | Rebuildable from IaC + source |
An “air gap” in a cloud is not a tape in a drawer, so the carrier’s air-gap copy is defined by trust, not distance: it lives in a dedicated backup account/subscription that production holds no standing management-plane rights over, sealed with Object Lock COMPLIANCE and its own KMS/HSM keys, reachable only through a break-glass, MUA-approved path. An attacker who has fully compromised the production tenant still cannot reach into that account to delete or re-key the copies — which is the entire point of the control.
Immutable copies are half a recovery capability; the other half is knowing how fast, and back to what point, each tier restores. The DR RTOs (1h/2h/8h/24h) describe failover to a live replica; a backup-driven clean rebuild is slower, because the team must first choose and validate a known-clean point, then restore into new infrastructure. The table quotes two restore points per tier: the normal one for everyday corruption, and the ransomware clean one, which may be hours or days older because its defining property is being provably uninfected, not most recent.
| Tier | Restore RTO | Normal restore point | Ransomware clean point | Method | Validation |
|---|---|---|---|---|---|
| Tier 0 | 1 hour | Last change | Last known-clean commit / key version | Key Vault undelete; redeploy IaC | Sign-in + PIM JIT test |
| Tier 1 | 4 hours | ≤ 15 min (log) | Last verified-clean daily full | PITR / HANA Backint into clean LZ | Transaction-set reconciliation |
| Tier 2 | 8 hours | ≤ 24 h (daily) | Last clean daily snapshot | Vault VM restore; object versioning | Count + checksum reconciliation |
| Tier 3 | 24 hours+ | ≤ 24 h | Last clean weekly | Rebuild from IaC + restore data | Smoke test of recovered service |
A restore SLA that has never been exercised is a number on a slide, so the carrier treats restore testing as a scheduled obligation with evidence. The cadence rotates through the estate so every tier and mechanism is proven on a known schedule, logged as a ServiceNow change and fed back into the runbooks; a recovery point is not “protected” until a restore has round-tripped it.
| Test | Scope | Frequency | Owner | Evidence |
|---|---|---|---|---|
| Item-level restore spot-check | Random files / DB rows | Weekly | Cloud Operations | Restored item + hash match |
| Database PITR drill | SoR + one OMS/WMS/TMS DB to scratch | Monthly | Platform Engineering | Reconciled row counts |
| SAP HANA restore | Backint restore to isolated subscription | Quarterly | Platform + SAP Basis | Consistent start + replay |
| Full-VM restore | Representative Tier-2 VMs, cross-region | Quarterly | Cloud Operations | Booted VM + smoke test |
| Clean-room ransomware rehearsal | Vend clean LZ, restore Tier-0/1 immutably | Semi-annual | Security & Identity + Ops | Runbook timing vs RTO |
| Immutability & lock attestation | Confirm Locked / COMPLIANCE across vaults | Monthly | Cloud Security & Identity | State outputs to evidence |
When the worst happens, improvisation is the enemy, so the ransomware path is a written, phased runbook that operationalises the “Ransomware / full region loss” row from the DR-runbooks section into the sequence an incident commander follows. Its governing principle is that recovery runs into a clean landing zone, never back into the blast radius — restoring into the compromised estate simply hands an attacker who may still hold persistence a freshly decrypted target. Each phase has a trigger, an action, its tooling, an owner, and an exit gate that must close before the next begins.
| Phase | Trigger / action | Tooling | Owner | Exit gate |
|---|---|---|---|---|
| 0 · Detect & declare | Correlated destructive event; commander declares ransomware | Sentinel, Wiz, CrowdStrike, ServiceNow | Security & Identity | Incident declared; scope estimated |
| 1 · Isolate & contain | Revoke sessions/tokens; disable federation; sever peering/TGW to affected accounts; freeze CI/CD | Entra / Okta, Conditional Access, NSG/SCP | Security & Identity + Network | Blast radius contained; no lateral path |
| 2 · Validate a clean restore point | Enumerate immutable RPs; scan for payload / IoC; pick newest provably clean per tier | AWS Backup / RSV catalog, AV/IoC scan, hash manifest | Cloud Operations | Named clean RP per Tier-0/1 |
| 3 · Restore into a clean landing zone | Vend fresh sub/account (no trust to the estate); restore from immutable / air-gap copy; rotate all secrets | Subscription/Account factory, Backup restore, Key Vault/KMS | Platform + Operations | Data restored + integrity-verified |
| 4 · Resume in tier order | Bring up 0 → 1 → 2 → 3; re-point edge/DNS; business sign-off before traffic | DNS failover, App Gateway/ALB, ServiceNow | Operations + App Enablement | Tier serving + validated |
| 5 · Forensics & closure | Preserve isolated images and immutable logs; root-cause; feed fixes into guardrails | Immutable Log Archive (S3 Object Lock), forensic images | Security & Identity | RCA signed; controls hardened |
Two properties make that runbook survivable. First, the immutable Log Archive is untouched by the attack — the Object-Lock evidence chain means the logs that prove what happened cannot be erased by the actor who did it. Second, the clean landing zone is vended, not repaired — the factories stamp out a governed, policy-inherited environment in under a business day, so the team recovers forward into a known-good estate rather than disinfecting the compromised one.
Protection carries a price, and the design keeps it honest by fitting inside the ≈ $35,000/month backup, DR and storage line the cost model already carries. The bill concentrates where the risk does — geo-redundant Azure and AWS backup storage holding the bulk of the recovery points, and the air-gapped Object-Lock copy that is the estate’s last line — with archive tiering pushing the seven-year retention down the cost curve so long-term compliance does not dominate the run-rate.
| Cost line | Driver | ~USD/month |
|---|---|---|
| Azure Backup — GRS vault + cross-region restore | VMs, SAP HANA, SQL-in-VM, PostgreSQL, Blob across 4 regions | $12,000 |
| AWS Backup — vault storage + cross-region copy | EBS, EC2, RDS, Aurora, DynamoDB, EFS, S3 across 4 regions | $9,000 |
| Air-gap S3 Object Lock (COMPLIANCE), isolated account | SoR, SAP, config — 7-year WORM copies | $5,000 |
| Long-term archive (Archive Blob / Glacier Deep Archive) | Customs / claims retention to 7 years | $3,500 |
| Cross-region + cross-cloud copy transfer | Inter-region and inter-account egress | $3,000 |
| Instant-restore snapshots + PITR storage | Short-window fast-restore tier | $2,500 |
| TOTAL | Immutable, cross-region, air-gapped backup estate | ≈ $35,000/month |
Read whole, the backup design is the estate’s admission that prevention is necessary but not sufficient: identity, segmentation, and posture reduce the probability of a destructive compromise but cannot drive it to zero, and the only honest answer to a determined ransomware actor is a set of copies they cannot reach, alter, or delete, restored into an estate they no longer control. Immutability makes the copies un-erasable, the air gap puts them beyond production’s reach, cross-region and cross-cloud spread survives the loss of a region or account, and rehearsed restores turn a stored artefact back into a running business — the difference, for a carrier moving temperature-controlled freight worldwide, between an incident and an extinction event.
Terraform and Ansible multi-stage CI/CD
Every layer above is delivered as code through a promotion pipeline that moves the same reviewed change from Dev to UAT to Staging to Production. Source lives in Bitbucket — Terraform modules, environment definitions, Ansible roles, and deployment manifests — behind mandatory pull-request review. Static validation runs first: Terraform fmt, validate, and lint, policy-as-code with tfsec or Checkov, and secrets scanning, all before a plan is allowed. Terraform applies against separate, locked, encrypted remote state per environment, region, and scope so a change in one environment can never corrupt another. Ansible then enforces OS, middleware, and SAP-host configuration from a dynamic Azure/AWS inventory using idempotent roles, with secrets injected at runtime rather than stored in code.
Two controls make this production-grade. Artifact-based promotion means the exact change validated in Dev is what reaches Production — environments stop drifting because they are no longer rebuilt differently each time. And manual approval gates before Staging and Production, with security and platform sign-off, give change governance a real enforcement point that ties into ServiceNow change records. The subscription and account factories feed the same pipeline, so even the creation of new landing zones is a reviewed, auditable code change.
DevSecOps software supply chain
The pipeline above is one slice of a larger software supply chain that the proposal asks to be engineered end to end. Reuse is enforced through a deliberate repository strategy: dedicated projects for Terraform modules, for YAML pipeline templates, for application code, for artifacts (npm, Maven, PyPI, NuGet, containers, generic), for policy-as-code, for Ansible roles, and for shared test assets. An application repository composes the shared modules and templates rather than duplicating them, so the platform team raises the standard once and every application inherits it on a controlled upgrade. Security runs across the whole path — SAST, secrets, SCA, and IaC/policy scanning (Wiz Code) early; build and dependency resolution through governed internal artifact repositories; container build, scan, and signing; Terraform plan and Ansible validation; functional, API, smoke, DAST, UI, integration, load, and resilience testing across the environments; database schema-as-code migrations that are versioned, reviewed, and approved like any other change; and mobile build, scan, and distribution through UEM test rings.
The principle running through it is traceability from source to production: every release path includes layered scanning and quality gates, every artifact is built once and promoted rather than rebuilt, and every production change — including database schema and mobile builds — carries provenance, approval, and a rollback plan. Treating endpoints and mobile delivery as pipeline-integrated release domains, and database changes as code, closes the two gaps that most enterprise pipelines leave open.
IoT connected logistics
The fleet is where the business meets the physical world, and IoT is a core platform capability rather than an add-on. Device and edge classes range from truck telematics and GPS to container low-power trackers, BLE/RFID asset tags, environmental sensors for temperature-sensitive cargo, and depot edge gateways that aggregate where direct cloud connectivity is unreliable. Every device carries a certificate-based identity and supports store-and-forward for intermittent links. Connectivity spans cellular, LPWAN, Wi-Fi, and cross-country roaming for 70-plus countries. Ingestion is deliberately split across both clouds: AWS IoT Core handles fleet-scale vehicle telemetry with its device registry, mutual TLS, and rules engine, while Azure IoT Hub carries the operational and control-tower telemetry that integrates with business processes.
Telemetry then fans out across three processing paths: a real-time path (Stream Analytics or Kinesis) for geofence, route-deviation, threshold-breach, and anomaly alerts; a near-real-time path feeding control-tower and customer visibility; and a historical path landing in the lakehouse for trend analysis, maintenance planning, and sustainability reporting. The platform integrates with SAP for shipment and asset context, with customer tracking portals, with ServiceNow for asset and incident workflows, and with notification services. The same zero-trust principles apply down to the device: unique identity per device, mutual authentication, certificate lifecycle management, a quarantine path for anomalous devices, and segmentation between device ingestion, management, analytics, and business networks. Hot, warm, and cold data zones keep live dashboards fast while preserving years of history for claims and emissions analysis.
Each cloud’s ingestion path earns its own deep dive. AWS is the primary for fleet-scale vehicle data: devices connect through AWS IoT Core with its rules engine, IoT FleetWise collects vehicle signals, Device Defender watches for anomalies, and Kinesis with Managed Service for Apache Flink drives the real-time path into Timestream and the lakehouse.
Azure is the primary for operational and control-tower integration: IoT Hub and the Device Provisioning Service handle device identity and twins, Stream Analytics runs the geofence and threshold rules, Logic Apps push events into SAP and ServiceNow, and Azure Data Explorer holds the queryable time-series history.
Observability and SOC integration
A platform this large is only operable if it is observable end to end. Dynatrace is the strategic observability plane, tracing a single request from the user through the CDN and WAF, into cloud ingress, the application, the database, and downstream integrations — the same path the edge and security diagrams describe, now instrumented. Around that topology sit Real User Monitoring for web apps, synthetic monitoring for customer portals and control towers, infrastructure and Kubernetes observability, and cost and capacity dashboards. Cloud-native telemetry from Azure Monitor, Log Analytics, and CloudWatch feeds the same plane. On the security side, cloud audit logs plus Wiz, CrowdStrike, and WAF signals flow into a SIEM (Microsoft Sentinel) and on into SOC workflows, with automated incident creation and enrichment in ServiceNow against the CMDB.
The outcome the business cares about is a closed loop: a degradation or an attack is detected, correlated, and turned into an enriched ServiceNow incident with the affected configuration item already attached, so the right team acts on context instead of a raw alert. Observability and security stop being separate consoles and become two views of the same telemetry, which is exactly what an estate spanning two clouds, four data centres, a global workforce, and a connected fleet needs.
Cost model and TCO
A reference architecture that cannot be costed is an academic exercise, so the design carries a budget envelope as a first-class artefact. The figures below are planning-grade estimates for a steady-state production estate running across four strategic regions in both Azure and AWS — expressed in USD per month, built bottom-up from the services each landing zone provisions, and meant to size a budget and frame FinOps conversations rather than to stand in for a vendor quote. They assume roughly one hundred applications live across the two clouds, reserved-instance and savings-plan commitments applied to steady-state compute, non-production environments scaled down outside business hours, and security and observability tooling priced at enterprise list less the discount a carrier of this size negotiates. Actual spend will move with traffic, device counts, data volumes, and the commercial terms struck with each provider; the value here is the shape of the spend — where the money concentrates and which levers move it — not a figure to the dollar.
Two characteristics of the model are worth calling out. First, application compute and the SAP landing zone together account for the largest share of the bill — a little under half of it — exactly where it should sit for a business whose value is in its applications and its SAP core rather than in undifferentiated platform plumbing. Second, the platform, edge, connectivity, and tooling lines are the price of running consistently and securely across two clouds and four data centres: dual circuits in every region, central inspection and posture management, and a single global edge are deliberate recurring costs the resilience and security requirements make non-negotiable rather than discretionary.
| Cost area | Drivers | ~USD/month |
|---|---|---|
| Hybrid connectivity | 8 ExpressRoute + 8 Direct Connect circuits (10 Gbps each) + gateways across 4 regions | $45,000 |
| Azure platform | Firewalls, gateways, Bastion, DNS, Sentinel, Log Analytics, Key Vault HSM, Backup | $60,000 |
| AWS platform | Network Firewall, Transit Gateway, NAT, GuardDuty, Security Hub, Control Tower, CloudWatch | $45,000 |
| Application compute & workloads | 100+ applications across both clouds (web, app, API, batch, container tiers) | $220,000 |
| SAP landing zone | HANA high-memory certified compute, HA across 2 AZs + asynchronous DR region | $120,000 |
| Data & integration platform | Lakehouse, event streaming, analytics and BI compute and storage | $85,000 |
| IoT platform | AWS IoT Core / Azure IoT Hub, streaming, time-series stores | $40,000 |
| Observability & security tooling | Dynatrace, Wiz, CrowdStrike Falcon, Okta licences | $95,000 |
| Backup, DR & storage | Geo-redundant backup, immutable archive, cross-region replication, object storage | $35,000 |
| Third-party edge | Global CDN, authoritative DNS, WAF | $25,000 |
| TOTAL | Steady-state production estate, 4 regions, dual-cloud | ≈ $770,000/month (~$9.2M/year) |
The model is not a fixed cost — it is an envelope the FinOps practice is expected to work against. The largest structural saving is commitment-based pricing: reserved instances and savings plans on the steady-state compute trim that base by roughly thirty per cent, which is why the compute lines assume committed rather than on-demand rates. On top of that sit operational levers the platform makes routine — autoscaling with a resilience floor so the estate never pays for idle headroom, scheduled shutdown of non-production outside business hours, continuous right-sizing driven by Dynatrace capacity signals, and storage lifecycle tiering that moves cold data down the cost curve automatically. Spend is made visible and accountable through tagging-driven showback and chargeback on the cost dashboards, so each team owns its consumption rather than the bill arriving as an undifferentiated lump, and the enterprise tooling agreements carry their own committed-volume discounts. One-time build and migration cost — discovery, landing-zone construction, the migration factory, and SAP cutover — is deliberately not folded into this run-rate; it is governed separately through the delivery roadmap and its waves so steady-state and programme economics never blur.
Bill of materials
Where the cost model says what the estate spends, the bill of materials says what the estate is — the resource inventory the subscription and account factories actually produce, and the concrete definition of what “done” looks like at the end of the build. It is deliberately expressed by landing zone and domain rather than as a flat resource list, because the value of the platform is that these resources arrive governed, repeatable, and identical across regions through code, not hand-built one ticket at a time. The counts below are the target-state footprint for the four-region dual-cloud design; per-application resources scale with the roughly one hundred workloads the onboarding factory vends, while the platform and shared-service rows are fixed regardless of how many applications land on top of them.
| Landing zone / domain | Key resources (count) |
|---|---|
| Azure platform | 4 regional hub VNets, 4 Azure Firewall Premium, 4 Application Gateway WAF v2, 8 ExpressRoute gateways (+ backup VPN gateways), Microsoft Sentinel, Key Vault HSM, Log Analytics, Recovery Services backup vaults |
| Azure identity & management | Identity, Connectivity, Management, and Security platform subscriptions; Entra Connect; Private DNS Resolver; Azure Monitor / Automation / Update Management |
| Azure workloads | Separate production and non-production subscriptions vended per application (≈100 applications), each hub-peered, policy-governed, and baseline-enrolled |
| AWS platform | 4 regional Transit Gateways, 4 AWS Network Firewall, Control Tower, Log Archive account (immutable S3 + Object Lock), Security Tooling account, GuardDuty + Security Hub org-wide |
| AWS network & shared services | Inspection / ingress / egress VPCs per region, Direct Connect Gateways, Shared Network and Shared Services accounts, IAM Identity Center |
| AWS workloads | Vended application accounts across Prod and Non-Prod OUs (≈100 applications), each TGW-attached, SCP-guarded, and baseline-enrolled |
| SAP landing zone | HANA production across 2 AZs (HSR synchronous) + warm-standby HANA in the paired DR region (asynchronous), clustered ASCS/ERS, dedicated backup and monitoring |
| Data & integration platform | Event Hubs / Kafka / Kinesis backbone, medallion lakehouse (raw / curated / governed + master-data band), data catalog, lineage and classification, analytics and BI |
| IoT platform | AWS IoT Core (fleet telemetry) + Azure IoT Hub (control-tower telemetry), Kinesis / Stream Analytics streaming, Timestream and Azure Data Explorer time-series stores |
| Connectivity | 8 ExpressRoute circuits + 8 Direct Connect circuits (10 Gbps, active-active BGP) across 4 regions, diverse carriers per region |
| Third-party edge | Global CDN, authoritative DNS with global traffic management and failover, WAF with OWASP / bot / API / rate-limit policy |
| Shared SaaS & tooling | Okta, Microsoft Entra ID, Wiz, CrowdStrike Falcon, Dynatrace, ServiceNow, Bitbucket, Microsoft Intune — operating horizontally across both clouds |
Read together, the two tables close the loop between intent and economics. The bill of materials enumerates the building blocks the factories emit on demand; the cost model prices them at steady state and points at the levers that keep the run-rate honest. Both are living artefacts — the inventory grows as applications onboard through the waves, and the spend is reforecast against actuals each cycle — but holding the design to an explicit, planning-grade number from the outset is what turns “a secure multi-cloud foundation” from an aspiration into something the business can budget, govern, and operate with its eyes open.
Operating model and RACI
The six teams plus the Cloud Center of Excellence only deliver a stable estate if their responsibilities are unambiguous; the most common cause of a missed RTO is not a technology failure but two teams each assuming the other owned the decision. The operating model therefore pins three things explicitly. Support runs as follow-the-sun, 24×7, tiered L1 → L2 → L3: the ServiceNow service desk (L1) triages and runs standard runbooks, Cloud Operations (L2) owns operational restoration and incident command, and Cloud Platform Engineering or the relevant vendor (L3) handles deep platform and product engineering. Change runs in three lanes — standard (pre-approved, automated), normal (CAB-reviewed within published change windows), and emergency (post-implementation review) — every one of which is raised and recorded against the CMDB in ServiceNow, with manual approval gates before Staging and Production tied to the change record. Patching follows a monthly cadence with critical out-of-band releases, honouring the patch SLA of 7 days for critical and 30 days for high-severity vulnerabilities surfaced by Wiz and the cloud-native scanners.
The RACI below allocates the load-bearing activities across the six teams. The recurring pattern is deliberate: the CCoE is accountable for standards and guardrails but delegates execution, security holds identity and incident authority, the platform team owns the factories and the build path, and Cloud Operations carries day-two run. One accountable (A) owner per row keeps decisions unambiguous; R is shared where genuine joint delivery is required.
| Activity | CCoE | Platform Eng | Security & Identity | Network | Operations | App Enablement |
|---|---|---|---|---|---|---|
| Landing-zone standards & reference architecture | A | R | C | C | I | C |
| Subscription / account vending (factory) | A | R | C | C | I | I |
| Policy / guardrail change (Policy + SCP) | A | R | R | C | I | I |
| Identity & PIM access approval | C | I | A/R | I | I | I |
| Network & firewall change | I | C | C | A/R | I | I |
| Application onboarding to the estate | C | C | C | I | I | A/R |
| Incident response & major-incident command | I | C | A/R | C | R | C |
| DR test execution & game-day | A | R | R | R | R | C |
| Cost management & FinOps | A | R | I | I | R | C |
| Patching & vulnerability remediation | I | R | A | I | R | C |
Migration and onboarding waves
A landing zone is only worth building if the estate can actually be moved into it without stalling, and that movement has to be sequenced rather than improvised. The estate is populated through a migration factory: a repeatable discovery-to-cutover pipeline in which every application is assessed, dispositioned, scheduled into a wave, and admitted only once the wave ahead of it has proven its foundations. The work is organised into six waves over roughly twenty-four months, each with explicit entry and exit gates so that no wave inherits half-finished plumbing from the one before it. The early waves deliberately deliver platform and low-risk workloads first, building operational muscle and confidence before the customer-facing, data, and SAP estates — the genuinely business-critical tier — are touched. The roadmap diagram sequences this progression; the table below pins the scope and gating of each wave.
| Wave | Months | Scope (representative) | Entry criteria | Exit criteria |
|---|---|---|---|---|
| W1 — Foundation and pilots | 0–3 | Landing zones, identity, hybrid connectivity, guardrails, and 2–3 pilot applications | Design signed off | Platform baseline live; DR Tier-0 tested |
| W2 — Shared services and low-risk internal | 3–6 | ~20 shared-services and low-risk internal applications | Wave 1 exit achieved | Operations runbooks live |
| W3 — Customer web and API | 6–10 | ~25 customer web and API workloads; third-party edge cutover | Security gates green | Edge live across both clouds |
| W4 — Data and integration | 9–12 | Lakehouse, streaming backbone, partner feeds | Data governance ready | Golden datasets served |
| W5 — SAP and high-criticality | 12–18 | SAP landing zone and remaining Tier-1 workloads | HANA sizing confirmed | SAP DR rehearsed |
| W6 — Optimisation and operating-model transition | 18–24 | Right-sizing, FinOps, handover to internal teams | Estate stable | Teams self-sufficient |
Disposition is decided per application at discovery using the standard 6R model — rehost, replatform, refactor, repurchase, retain, or retire — so that each workload earns its place in a wave rather than being lifted wholesale. In practice most internal applications fall to rehost or replatform, taking the path of least friction onto the new platform; the customer-facing and data workloads are where refactoring concentrates, because cloud-native rebuild is what unlocks their scale and resilience targets; and a meaningful tail of legacy systems is retired outright rather than carried forward, shrinking the estate that the operating model ultimately has to run.
Architecture decision records
Reviewers should be able to see the trade-offs behind the design, not just its conclusions. Each load-bearing decision is therefore captured as an architecture decision record that names the alternative that was rejected and the reasoning that settled it — so the design can be interrogated, and so future teams understand which doors were deliberately closed and why. The records below are the ten decisions that shape the estate most; several of them, notably the dual-cloud posture and the split IoT platform, are direct consequences of the business mandate rather than free engineering choices, and the records make that provenance explicit.
| ID | Decision | Alternative considered | Rationale / trade-off |
|---|---|---|---|
| ADR-01 | Dual-cloud landing zone across Azure and AWS | Single-cloud consolidation | Business mandate plus resilience and best-of-breed services; accepts higher operational complexity, mitigated by one common guardrail set across both clouds |
| ADR-02 | Azure hub-and-spoke topology | Azure Virtual WAN | Greater control, lower cost, and team maturity at current scale; Virtual WAN to be revisited as the estate grows |
| ADR-03 | AWS Transit Gateway with a central inspection VPC | VPC peering mesh | Central policy enforcement and clean scaling, avoiding the unmanageable sprawl of a full peering mesh |
| ADR-04 | Okta as federation broker with Microsoft Entra ID as control plane | Entra ID only | Preserves the existing SaaS estate already standardised on Okta and brokers AWS federation cleanly |
| ADR-05 | Third-party global edge for CDN, DNS, and WAF | Per-cloud Azure Front Door plus CloudFront | A single cross-cloud control point with health-checked failover between clouds, rather than two disjoint edges |
| ADR-06 | SAP self-managed on certified IaaS | RISE with SAP | Retains control, leverages existing operations skills, and keeps integration close; RISE deferred, not ruled out |
| ADR-07 | Split IoT — AWS IoT Core for fleet, Azure IoT Hub for control tower | Single unified IoT platform | Best-fit service per workload and consistent with the dual-cloud mandate |
| ADR-08 | Terraform with Ansible as the IaC and configuration standard | Native Bicep plus CloudFormation | Cross-cloud consistency from one toolchain instead of two divergent native stacks |
| ADR-09 | Factory-based vending — AFT plus an Azure subscription factory | Manual provisioning | Governed, fast, and repeatable account and subscription delivery within a business day |
| ADR-10 | Centralised egress inspection per region | Per-VNet and per-VPC firewalls | One policy enforcement point per region, avoiding inconsistent rule sets scattered across every network |
Risks, assumptions, issues and dependencies
A programme of this scope is governed against a live RAID register, reviewed continuously rather than written once and shelved. Risks are tracked with owners and mitigations; assumptions are stated so that, if they break, the impact is visible immediately; open issues are worked to closure; and external dependencies are surfaced early, because several of them — circuit delivery and SAP Basis capacity in particular — sit on the critical path and can move whole waves if they slip. The register below is the working view the programme governs against.
| Type | Item | Impact | Mitigation / owner |
|---|---|---|---|
| Risk | Cross-cloud skills gap across delivery teams | Slower delivery; operational errors | Structured enablement and handover plan; CCoE |
| Risk | Circuit-provider concentration in a region | Correlated connectivity loss | Diverse carriers per region; Network and Connectivity |
| Risk | SAP migration complexity | Tier-1 cutover delay or instability | Early sizing and rehearsed failover; Platform plus SAP Basis |
| Risk | Cost overrun against the budget envelope | Programme funding pressure | FinOps guardrails and budgets; CCoE |
| Risk | IoT ingest spikes beyond plan | Telemetry loss or back-pressure | Autoscale and load test to 50,000 msg/sec; Platform |
| Risk | Data-residency breach | Regulatory and contractual exposure | Lakehouse governance band enforcement; Security and Identity |
| Risk | Tooling lock-in | Reduced future flexibility | Infrastructure-as-code and portability discipline; Platform |
| Assumption | The four on-prem data centres persist as first-class regions | Network and DR design depend on it | Validated with infrastructure owners |
| Assumption | The dual-cloud mandate is fixed | Whole topology assumes it | Confirmed with business sponsors |
| Assumption | Workday remains the authoritative HR source | Joiner-mover-leaver identity flow depends on it | Confirmed with HR and Identity teams |
| Assumption | Circuit lead times are met | Wave 1 foundation timing depends on it | Tracked against procurement milestones |
| Issue (open) | Circuit procurement pending | Blocks connectivity baseline | Expedited ordering; Network and Connectivity |
| Issue (open) | SAP sizing to be confirmed | Gates Wave 5 entry | Sizing exercise in progress; Platform plus SAP Basis |
| Issue (open) | Country data-residency list to finalise | Governance rules incomplete | Legal and compliance review under way; Security and Identity |
| Dependency | Carrier circuit delivery | Foundation and connectivity | External telecom carriers |
| Dependency | SAP Basis team availability | Wave 5 SAP migration | Internal SAP Basis function |
| Dependency | Okta and Workday integration | Identity lifecycle automation | Identity team plus vendors |
| Dependency | Security-tooling licences | Posture, EDR, and SIEM coverage | Procurement and vendors |
Delivery roadmap and acceptance
The work is sequenced as Cloud Adoption Framework workstreams so the engagement has clear governance: Strategy and Plan (portfolio discovery, dependency mapping, country and data-residency constraints, SAP assessment), Ready (landing-zone build, identity foundation, connectivity, guardrails, operations baseline), Adopt (migration in waves), and Govern and Manage (policy, cost, monitoring, backup, DR, incident response). Migration itself runs as the six waves detailed above — each gated and sequenced on the delivery-roadmap timeline — so the Adopt workstream proceeds through explicit checkpoints rather than as an open-ended lift.
The architecture is complete only when it demonstrates the acceptance criteria the business set: a fully defined Azure and AWS landing zone with clean platform/application separation; a zero-trust identity model covering office and home users with SSO, MFA, Conditional Access, and PIM; resilient hybrid connectivity from all four data centres with dual ExpressRoute and dual Direct Connect; multi-layer security spanning a third-party edge, cloud posture, EDR, observability, and policy-as-code; a tested HA and DR design for critical services including SAP; a scalable onboarding model for 100-plus applications; and a governance and operations model the internal teams can run after handover. Twenty-seven diagrams do not make a design correct on their own — but a design that can be drawn this precisely, decision by decision, is one a consulting partner can actually build, and one the logistics business can actually operate.