By the end of this you have a landing resource group, an onboarding identity with almost no power, the right resource providers registered, and a repeatable connect delivered by Group Policy or Configuration Manager that brings a fleet of Windows Servers into that group as Connected, correctly tagged machines, each proven manageable by a real action driven from the cloud rather than merely green in the portal. It is written against the current portal and the mid-2026 agent, and this corner of the platform moves, so confirm the labels and flags before a large rollout.
Before Step 1: you need an Azure subscription where you can register resource providers and create role assignments (Owner or User Access Administrator to grant the role, and Application Administrator or Cloud Application Administrator to create the app registration), a resource group to hold the machines, and outbound HTTPS 443 from each server to the Arc endpoints, or an Arc gateway or Private Link Scope if you do not allow broad egress. On each server: local administrator, Windows Server 2012 R2 or later, and PowerShell 5.1 or newer. Each step below gives the portal walk-through first, then the same thing as a script for the reader who is onboarding at scale.
Step 1: Register the providers and choose the landing zone
Decide where Arc machines land before you connect the first one, because the resource group is a security boundary and the region is effectively permanent. In the portal, open Subscriptions, select the subscription, and under its Settings open Resource providers. Register Microsoft.HybridCompute, Microsoft.GuestConfiguration, Microsoft.HybridConnectivity, and Microsoft.Compute. Add Microsoft.AzureArcData only if these machines also run SQL Server you intend to Arc-enable. Then create the resource group the machines will land in, in the region where their data will reside.
| Setting | Value | Why |
|---|---|---|
| Resource providers | Microsoft.HybridCompute, Microsoft.GuestConfiguration, Microsoft.HybridConnectivity, Microsoft.Compute | Agent projection, machine configuration, remote access, and (Microsoft.Compute) Update Manager plus automatic extension upgrade each need their provider registered. Microsoft.Compute is the one most people miss, and it breaks patching later, not at connect time. |
| Landing resource group | Dedicated, per environment or per trust tier | Write access here equals code execution on every machine inside it. Scope it as a boundary, not as a folder. |
| Region | Chosen once, aligned to data residency | Machine and extension data stays in the region you pick, and moving later is disruptive. |
The same as a script, which is how you would do it across many subscriptions:
az account set --subscription "<sub-id>"
az provider register --namespace Microsoft.HybridCompute
az provider register --namespace Microsoft.GuestConfiguration
az provider register --namespace Microsoft.HybridConnectivity
az provider register --namespace Microsoft.Compute
az group create --name "rg-arc-servers" --location "eastus"
Expected result: az provider show -n Microsoft.HybridCompute --query registrationState -o tsv reads Registered within a few minutes. Failure mode: if you run the onboarding before a provider reads Registered, azcmagent connect fails with a provider-not-registered error, and the fix is to register and wait rather than to retry the connect.
Step 2: Create the onboarding identity, scoped and short-lived
Create a dedicated identity for onboarding and give it the purpose-built role, never Contributor. In the portal, open Microsoft Entra ID, go to App registrations, and register an application named arc-onboarding-sp. On that app, open the Certificates and secrets pane and add a client secret with a short expiry. Then open your landing resource group, go to its Access control (IAM), and add a role assignment granting that application the Azure Connected Machine Onboarding role, and nothing else. That role can create machine resources and cannot delete a machine or manage extensions, which is exactly the power you want in a credential you are about to distribute to every server.
| Setting | Value | Why |
|---|---|---|
| Role | Azure Connected Machine Onboarding | Create-only. A leaked onboarding secret produces junk resources, not code execution across the fleet. Deleting and managing machines is a separate, higher role held by people. |
| Scope | The landing resource group, not the subscription | Narrow the blast radius of a credential you are about to scatter to every server. |
| Secret | Short expiry, stored in a protected variable or Key Vault, rotated on a schedule | The secret rides in the onboarding payload; treat it as perishable and never commit it to source. |
The same as a script, which creates the app registration, the service principal, and the scoped role assignment in one call:
az ad sp create-for-rbac \
--name "arc-onboarding-sp" \
--role "Azure Connected Machine Onboarding" \
--scopes "/subscriptions/<sub-id>/resourceGroups/rg-arc-servers"
This prints an appId, a password, and your tenant. Failure mode: granting Contributor “to save time” is the mistake to avoid, because onboarding needs only the create-machine permission, and the wider role turns a distributed secret into fleet-wide reach for no benefit. The management counterpart, Azure Connected Machine Resource Administrator, is a different role you assign to administrators, not to the onboarding identity.
Step 3: Choose the connectivity model, then pre-flight it
Decide how the agent reaches Azure before you deploy, because retrofitting connectivity across a live fleet is painful. Each option is a real trade of simplicity against isolation, and each has a seam worth naming out loud.
| Model | Use when | The seam to plan for |
|---|---|---|
| Direct, public 443 | Standard egress is available and inspection is light | A long endpoint list, and TLS inspection must be exempted for the Arc traffic or it breaks the agent. As of April 2026 the AzureFrontDoor.Frontend service tag is also required, alongside AzureArcInfrastructure. |
Arc gateway (attached with --gateway-id) | You want a short, auditable allowlist. Generally available since agent 1.58. | About seven FQDNs instead of the full list, but roughly 2,000 servers per gateway and public cloud only. Not recommended behind TLS inspection. |
Private Link Scope (attached with --private-link-scope) | Regulatory isolation of the Arc service endpoints | Entra sign-in, Resource Manager, and package downloads still use public endpoints, so it is not total isolation. Validate with the private-link check below. |
Note that the proxy is configured separately from the connect, not as a connect flag. On a machine that egresses through a proxy, set it on the agent with azcmagent config set proxy.url "http://proxy.host:port" (or the HTTPS_PROXY environment variable), and bypass specific services if needed with azcmagent config set proxy.bypass "AAD,ARM". Then pre-flight the connectivity from the server’s own network position before you connect anything:
azcmagent check --location "eastus" --cloud "AzureCloud"
# add --enable-pls-check when validating a Private Link Scope
Expected result: every required endpoint reports reachable true, and the output shows whether traffic went direct, through the proxy, or through the gateway. Failure mode: a proxy or firewall with no exception shows a reachable false here, which is far cheaper to find now than after a failed connect on five hundred machines.
Step 4: Onboard at scale
This is the step that is inherently at scale, so the script leads and the portal is the footnote. Every server runs the same azcmagent connect, and this is where the service principal, your connectivity choice, and the tags all come together. Decide the tags now, because they are how every later article scopes policy, patching, and Defender to exactly these machines.
azcmagent connect \
--service-principal-id "<app-id>" \
--service-principal-secret "<secret>" \
--resource-group "rg-arc-servers" \
--tenant-id "<tenant-id>" \
--subscription-id "<sub-id>" \
--location "eastus" \
--cloud "AzureCloud" \
--tags "Environment=Production,Datacenter=HQ,ArcOnboarded=2026" \
--gateway-id "<arc-gateway-resource-id>" # omit if not using the gateway
To deliver that across a domain, use the Group Policy tooling Microsoft publishes as the ArcEnabledServersGroupPolicy project. Stage the agent MSI and the onboarding scripts on a network share the machines can read (domain computers get Change, administrators Full Control), then run DeployGPO.ps1 to create a GPO named along the lines of “[MSFT] Azure Arc Servers” that schedules a task running the idempotent EnableAzureArc.ps1, which skips any machine already connected. Link that GPO to an organizational unit of server computer objects, never the domain root, and disable it once the fleet is connected so it does not keep re-running at every reboot. Configuration Manager is the other supported at-scale path, either as a PowerShell script deployment or a custom task sequence to a device collection.
For a single machine or a small pilot, the portal is where this same connect lives: open Azure Arc, go to Infrastructure, then Machines, choose Onboard existing machines, and step through Basics, Tags, and Download and run script. That wizard generates an OnboardingScript.ps1 you run locally with interactive sign-in, no service principal involved. It is the right tool for one machine and the wrong tool for forty, which is the whole reason the script leads this step.
Step 5: Harden the agent before the fleet grows
The agent’s local controls are the one authority the cloud cannot override, and the moment to set them is at build, through the same configuration management that delivers everything else. On a sensitive server, constrain the extension surface to what you actually use, or put the agent in monitor mode where only Microsoft-managed tooling is allowed and remote access and in-guest policy are off. Azure cannot read these settings, so they belong in your configuration baseline, not in a portal you check later.
# Allow only the extensions you rely on
azcmagent config set extensions.allowlist "Microsoft.Azure.Monitor/AzureMonitorWindowsAgent"
# Or lock the machine to Microsoft-managed tooling only
azcmagent config set config.mode monitor
Failure mode: a hardening decision made by hand on one machine drifts the moment someone rebuilds it. Enforce these through configuration management so the veto survives a reinstall, because a blocklist that lapses silently is worse than none.
Step 6: Verify, and prove management works end to end
A green Connected status is necessary but not sufficient. Confirm the agent is healthy locally, confirm the machine shows Connected in Azure, and then prove the management plane actually reaches inside the guest by driving one real action from the cloud.
azcmagent show # Agent status Connected, plus subscription, resource group, name, version
azcmagent check # Re-confirms reachability from the machine's own network position
The end-to-end test is the part that separates a projected resource from a managed server: from Azure, assign a simple audit-only machine configuration to the machine, or install a benign extension such as the Azure Monitor Agent, and confirm it reports back. If the guest responds to something driven entirely from the cloud, and the machine shows Connected in the resource group with the tags you set, onboarding is genuinely complete.
A worked example
Take a forty-server branch fleet that egresses only through a filtering proxy. The servers sit in an organizational unit named OU=BranchServers. You add the Arc endpoints to the proxy allowlist (or stand up an Arc gateway and use its seven-FQDN set), stage the agent MSI and scripts on the branch file share, and set the proxy on the connect script with azcmagent config set proxy.url before the connect line. You run DeployGPO.ps1 to create the onboarding GPO, link it to OU=BranchServers, and tag the machines Datacenter=Branch. At the next reboot each server installs the agent, connects through the proxy, and lands in rg-arc-servers tagged Datacenter=Branch, ready for a policy that targets that tag. A separate twelve-server fleet in AWS skips Group Policy entirely: you use the Multicloud connector enabled by Azure Arc, whose Arc Onboarding solution discovers the EC2 instances and installs the agent for you (generally available for AWS; GCP is still preview). Same destination, delivered the way each environment actually reaches it.
Naming convention
Keep these identical across every server so policy, Update Manager, and Defender can target by tag rather than by a hand-maintained list. Resource group rg-arc-servers, or rg-arc-servers-<region> if you split by region. Onboarding application arc-onboarding-sp. Tags Environment, Datacenter, and an ArcOnboarded year stamp. Leave the GPO name as the tool generates it so it is recognizable as Microsoft-authored onboarding rather than something local.
Rolling it back
Onboarding is the step people most want a documented exit from, because it is the one that grants code execution. The exit is clean, but the order matters more here than anywhere else in the series: extensions must come off before the agent goes, or you strand them.
- Remove any VM extensions from the machine first. Uninstalling the agent does not remove extensions, and extensions left behind are not recognized if you ever reconnect the server, so this is the step that saves you a confusing afternoon later.
- Disconnect the machine:
azcmagent disconnect, run locally as an administrator. This deletes the corresponding Azure resource and clears the agent’s local state in one action. If the Azure resource was already deleted, useazcmagent disconnect --force-local-onlyto clear the local side only. - Uninstall the agent software itself if the machine is leaving Arc for good, rather than leaving a disconnected agent installed and idle on the box.
- Revert the delivery mechanism, or it will simply re-onboard the machine at the next policy refresh. Unlink or delete the ArcEnabledServersGroupPolicy object from the server OU, or retire the Configuration Manager deployment, before you disconnect anything at scale. Rolling back one machine while the GPO is still linked is a race you will lose.
- Delete the onboarding service principal and its role assignment. The Azure Connected Machine Onboarding role scoped to the landing resource group has no reason to outlive the onboarding campaign, and a stale onboarding secret is exactly the kind of credential that turns up in an audit two years later.
- Delete the Azure resource if a disconnect left one behind, and then decide deliberately about the landing resource group itself: an empty resource group is harmless, but the role assignments on it are not, so clear those too.
Before you scale it
- Providers registered and reading Registered, Microsoft.Compute included, and the landing resource group scoped as a trust boundary.
- Onboarding identity created with the Azure Connected Machine Onboarding role, scoped to the resource group, secret short-lived and stored protected.
- Connectivity model chosen, its seam accounted for in the firewall, proxy set on the agent if needed, and
azcmagent checkall-green from the server’s own network. - Connect finalized with the resource group, location, tenant, subscription, and the tag scheme every later article will target, plus
--gateway-idif using the gateway. - Delivery idempotent and scoped to a server OU or a device collection, not the whole estate, and the GPO disabled once the fleet is connected.
- Agent hardening set through configuration management, because Azure cannot attest it.
- End-to-end test passed:
azcmagent showConnected, the machine present and tagged in the resource group, and one cloud-driven action confirmed inside the guest.
With machines connected and proven manageable, the next article leaves onboarding behind and takes up the question the whole platform turns on for a server team: whether Azure Policy and machine configuration are a real replacement for Group Policy, and exactly where that claim stops being true.
Azure Arc
‹ Previous: [ARC 2] Onboarding: The Agent Is a Trust Decision
Next: [ARC 3] Policy and Machine Configuration: The GPO Question, Answered Honestly ›




