This is the companion to the starting-point article, and its job is narrow and non-negotiable: before you deploy a single management group, you inventory what already exists, because you cannot safely govern or migrate what you have not first understood. In the Microsoft 365-only case this pass is short. In the accidental-infrastructure and hybrid cases it is where you find the landmines, and the most dangerous one, a catch-all DNS forwarder, will not announce itself until a private endpoint silently resolves to the wrong address months after go-live. Run every step. Write the output down. The discovery document you produce here is the input to every decision that follows.
Prerequisites: Reader access to any existing Azure subscriptions, the Azure CLI signed in (az login), and, for the hybrid case, an account that can query the on-premises DNS servers. Everything here is read-only. You are looking, not changing.
Step 1: Inventory every subscription
Start with what subscriptions exist, who owns them, and how they are billed, because a pay-as-you-go subscription is a migration target and its billing needs to change before it becomes a permanent part of the estate. In the portal this is the Subscriptions blade. From the command line:
az account list --output table
az account list --query "[].{Name:name, State:state, Id:id}" --output table
For each subscription, record the offer type (whether it is pay-as-you-go, a Cloud Solution Provider subscription, or an Enterprise Agreement), the named owner, and a one-line description of what actually runs in it. You are building a table like this for the worked example, Northfork Supply Co.:
| Subscription | Billing | Owner | Contents | Disposition |
|---|---|---|---|---|
| Northfork-PAYG | Pay-as-you-go | former admin | 2 VMs, a VPN gateway, loose resource groups | Migrate, then retire |
| (none for non-prod) | n/a | n/a | n/a | Create later on trigger |
The disposition column is the point of the exercise. By the end of discovery you know which subscriptions become the foundation, which get migrated in, and which get retired.
Step 2: Map the network and its address space
The single most important fact discovery produces is the complete set of address ranges already in use, on both sides of the connection, because the foundation’s address plan must not overlap with any of them and overlap is expensive to fix once workloads are live. List every existing virtual network and its prefixes:
az network vnet list --query "[].{Name:name, RG:resourceGroup, Space:addressSpace.addressPrefixes}" --output table
az network vnet subnet list --resource-group RG-NAME --vnet-name VNET-NAME --output table
Then record the on-premises ranges by hand, because the CLI cannot see them: every subnet in the office, the datacenter, and any remote sites reachable across the existing tunnel. Write them into one address register alongside the Azure ranges. The check you are performing is simple and absolute: no proposed foundation range may overlap an existing range on either side. For Northfork, the on-premises estate uses 172.16.0.0/16 and the accidental Azure VNet sits at 10.0.0.0/24, which is why the foundation in later articles is planned into 10.40.0.0/16, clear of both.
Step 3: Record the connectivity in place
Document how on-premises and Azure are connected today, or whether they are connected at all. Note the connection type (site-to-site VPN or ExpressRoute), the on-premises device it terminates on and who manages it, and whether any client-side connectivity product such as a SASE service is in play. This matters because the network article treats client connectivity and server-to-server infrastructure connectivity as two separate problems, and you need to know which you already have.
| Item | Value | Why it matters |
|---|---|---|
| On-prem to Azure link | Site-to-site VPN on a Fortinet appliance | Carries infrastructure traffic; the on-prem side is configured by hand later |
| Client connectivity | SASE agent, backhauls to the office | Candidate to re-home so clients stop hairpinning through the datacenter |
| ExpressRoute | None | VPN gateway is the correct choice at this scale |
Step 4: Audit access assignments
In an accidental environment, role assignments were handed to individual people, and the identity article insists that access attach to groups instead. Find every direct user assignment now so you know the scope of the cleanup:
az role assignment list --all --include-inherited \
--query "[?principalType=='User'].{User:principalName, Role:roleDefinitionName, Scope:scope}" \
--output table
Flag anything at subscription scope, and flag every standing Owner. Both are corrected in the identity build. You are not fixing them here; you are counting them so the later remediation has a defined list rather than a vague intention.
Step 5: Capture the Secure Score baseline
Record the current Microsoft Defender for Cloud Secure Score before you change anything, because the value of the foundation is partly demonstrated by the movement of this number, and you cannot show movement from a baseline you never wrote down. The portal shows it on the Defender for Cloud overview. From the command line, with the account extension available:
az security secure-scores list --query "[].{Name:name, Current:properties.score.current, Max:properties.score.max}" --output table
Note the number and the date. Set a target and a review cadence in the operating article; the baseline is what makes that target meaningful.
Step 6: Inventory the managed detection provider
Many organizations at this scale already pay an external managed detection and response provider, and that provider is usually pulling from Microsoft signals already. Before you decide what your own Log Analytics workspace ingests, ask the provider two questions: which log sources do you collect, and how long do you retain them. The answer changes what you duplicate. It never changes the rule that you keep a local ninety-day floor regardless, but it does let you use cheaper tiers for anything the provider already retains long-term. Record the provider name, the collected sources, and the retention period.
Step 7: Audit on-premises DNS for catch-all forwarders
This is the step nobody runs and everybody should. In the hybrid case, once private endpoints exist, on-premises devices resolve Azure private names through conditional forwarders that point at the private DNS resolver. That chain breaks, silently and completely, if any on-premises DNS server already has a broad forwarder or stub zone for a Microsoft domain, because that broad rule intercepts the private DNS queries before your specific rules ever see them. The symptom appears months later as a private endpoint resolving to a public address, and it is the single hardest post-deployment problem to diagnose. So you find these entries before you deploy anything, not after.
On each on-premises DNS server, run the DNS Server PowerShell module and look for any conditional forwarder, stub zone, or manual record referencing windows.net, core.windows.net, azure.com, or microsoft.com:
Get-DnsServerZone | Where-Object { $_.ZoneType -eq 'Forwarder' } |
Select-Object ZoneName, MasterServers
Get-DnsServerForwarder
Get-DnsServerZone | Where-Object {
$_.ZoneName -match 'windows\.net|core\.windows\.net|azure\.com|microsoft\.com'
} | Select-Object ZoneName, ZoneType
The first command lists conditional forwarders. The second shows the server-level forwarders, where a single catch-all pointing all unknown queries at a device that mishandles Azure names does the most damage. The third catches stub zones and manual zones for the Microsoft domains. Record every match. If you find a broad forwarder for one of these domains, it has to be narrowed or removed before private endpoints go in, and that finding belongs at the top of the discovery document in bold, because the private DNS build depends on it.
Validation: the completed discovery document
Discovery is done when you can answer all of the following from a written document rather than from memory:
- Every subscription, its billing model, owner, contents, and disposition.
- A single address register listing every in-use range on both sides, with a proposed non-overlapping foundation range confirmed clear.
- The connectivity in place, split into infrastructure and client connectivity.
- A list of direct user role assignments and every standing Owner.
- The Secure Score baseline value and date.
- The managed detection provider’s collected sources and retention.
- The result of the DNS forwarder audit, with any catch-all entries flagged for remediation.
End-to-end test
Two checks confirm the discovery is trustworthy. First, take the proposed foundation address range and compare it against every entry in the address register; if it overlaps nothing on either side, the range is safe to build on. Second, confirm the DNS audit came back either clean or with every catch-all entry explicitly listed for remediation. If both hold, you have removed the two failure modes that most often surface long after deployment, and the build can begin with the governance backbone.
Azure Landing Zones
‹ Previous: [LZ 2] Where You Are Starting From
Next: [LZ 3] The Governance Backbone ›




