Back to Blog
5 min read

Azure Arc Updates - Hybrid Cloud Management

Azure Arc extends Azure management capabilities to any infrastructure, including on-premises, multi-cloud, and edge environments. This post covers the latest Azure Arc updates and how to leverage them for hybrid cloud scenarios.

Azure Arc Overview

Arc-Enabled Servers

# Connect a server to Azure Arc
# Download and run the connection script from Azure Portal

# Or use Azure CLI
az connectedmachine connect \
    --resource-group "HybridServers" \
    --name "WebServer01" \
    --location "eastus"

# List connected machines
az connectedmachine list \
    --resource-group "HybridServers" \
    --output table

# Show machine details
az connectedmachine show \
    --resource-group "HybridServers" \
    --name "WebServer01"

Arc-Enabled Kubernetes

# Connect a Kubernetes cluster to Azure Arc
az connectedk8s connect \
    --resource-group "HybridK8s" \
    --name "OnPremCluster" \
    --location "eastus" \
    --kube-config ~/.kube/config

# Verify connection
az connectedk8s show \
    --resource-group "HybridK8s" \
    --name "OnPremCluster"

# List connected clusters
az connectedk8s list \
    --resource-group "HybridK8s" \
    --output table

# Enable cluster extensions
az k8s-extension create \
    --resource-group "HybridK8s" \
    --cluster-name "OnPremCluster" \
    --cluster-type connectedClusters \
    --name "azuremonitor-containers" \
    --extension-type Microsoft.AzureMonitor.Containers

Arc-Enabled Data Services

# Create data controller
az arcdata dc create \
    --name "arc-dc" \
    --resource-group "ArcData" \
    --location "eastus" \
    --connectivity-mode "indirect" \
    --k8s-namespace "arc" \
    --storage-class "default" \
    --infrastructure "onpremises"

# Create Arc-enabled SQL Managed Instance
az sql mi-arc create \
    --name "sql-mi-arc" \
    --resource-group "ArcData" \
    --location "eastus" \
    --custom-location "onprem-location" \
    --storage-class-data "default" \
    --storage-class-logs "default" \
    --cores-limit 4 \
    --memory-limit "8Gi"

# Create Arc-enabled PostgreSQL
az postgres arc-server create \
    --name "postgres-arc" \
    --resource-group "ArcData" \
    --custom-location "onprem-location" \
    --storage-class-data "default" \
    --storage-class-logs "default"

Managing Arc Resources

Azure Policy for Arc

// Policy to require tags on Arc-enabled servers
{
    "mode": "Indexed",
    "policyRule": {
        "if": {
            "allOf": [
                {
                    "field": "type",
                    "equals": "Microsoft.HybridCompute/machines"
                },
                {
                    "field": "tags['Environment']",
                    "exists": false
                }
            ]
        },
        "then": {
            "effect": "deny"
        }
    }
}
# Apply policy to Arc-enabled servers
az policy assignment create \
    --name "require-env-tag" \
    --scope "/subscriptions/{subscription-id}/resourceGroups/HybridServers" \
    --policy "require-environment-tag"

# View compliance
az policy state list \
    --resource-group "HybridServers" \
    --filter "complianceState eq 'NonCompliant'"

Monitoring Arc Resources

using Azure.Monitor.Query;
using Azure.Identity;

public class ArcMonitoringService
{
    private readonly LogsQueryClient _logsClient;
    private readonly MetricsQueryClient _metricsClient;

    public ArcMonitoringService()
    {
        var credential = new DefaultAzureCredential();
        _logsClient = new LogsQueryClient(credential);
        _metricsClient = new MetricsQueryClient(credential);
    }

    public async Task<List<ArcServerMetrics>> GetArcServerMetricsAsync(string workspaceId)
    {
        var query = @"
            Heartbeat
            | where ResourceProvider == 'Microsoft.HybridCompute'
            | summarize LastHeartbeat = max(TimeGenerated) by Computer, OSType, Version
            | project Computer, OSType, Version, LastHeartbeat,
                      Status = iff(LastHeartbeat > ago(5m), 'Online', 'Offline')
            | order by Computer asc";

        var response = await _logsClient.QueryWorkspaceAsync(
            workspaceId,
            query,
            new QueryTimeRange(TimeSpan.FromDays(1)));

        var metrics = new List<ArcServerMetrics>();
        foreach (var row in response.Value.Table.Rows)
        {
            metrics.Add(new ArcServerMetrics
            {
                Computer = row["Computer"].ToString(),
                OSType = row["OSType"].ToString(),
                Version = row["Version"].ToString(),
                LastHeartbeat = (DateTime)row["LastHeartbeat"],
                Status = row["Status"].ToString()
            });
        }

        return metrics;
    }

    public async Task<List<ArcK8sMetrics>> GetArcK8sMetricsAsync(string workspaceId)
    {
        var query = @"
            KubeNodeInventory
            | where ClusterName startswith 'arc-'
            | summarize by ClusterName, Computer, Status, KubeletVersion
            | project ClusterName, NodeName = Computer, Status, KubeletVersion";

        var response = await _logsClient.QueryWorkspaceAsync(
            workspaceId,
            query,
            new QueryTimeRange(TimeSpan.FromHours(1)));

        var metrics = new List<ArcK8sMetrics>();
        foreach (var row in response.Value.Table.Rows)
        {
            metrics.Add(new ArcK8sMetrics
            {
                ClusterName = row["ClusterName"].ToString(),
                NodeName = row["NodeName"].ToString(),
                Status = row["Status"].ToString(),
                KubeletVersion = row["KubeletVersion"].ToString()
            });
        }

        return metrics;
    }
}

public class ArcServerMetrics
{
    public string Computer { get; set; }
    public string OSType { get; set; }
    public string Version { get; set; }
    public DateTime LastHeartbeat { get; set; }
    public string Status { get; set; }
}

public class ArcK8sMetrics
{
    public string ClusterName { get; set; }
    public string NodeName { get; set; }
    public string Status { get; set; }
    public string KubeletVersion { get; set; }
}

GitOps with Arc

# flux-config.yaml - GitOps configuration
apiVersion: v1
kind: Namespace
metadata:
  name: flux-system
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
  name: app-repo
  namespace: flux-system
spec:
  interval: 1m
  url: https://github.com/myorg/k8s-configs
  ref:
    branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: app-kustomization
  namespace: flux-system
spec:
  interval: 10m
  sourceRef:
    kind: GitRepository
    name: app-repo
  path: ./clusters/production
  prune: true
# Enable GitOps on Arc-enabled cluster
az k8s-configuration flux create \
    --resource-group "HybridK8s" \
    --cluster-name "OnPremCluster" \
    --cluster-type connectedClusters \
    --name "gitops-config" \
    --namespace "flux-system" \
    --scope cluster \
    --url "https://github.com/myorg/k8s-configs" \
    --branch "main" \
    --kustomization name=app path=./clusters/production prune=true

# Check configuration status
az k8s-configuration flux show \
    --resource-group "HybridK8s" \
    --cluster-name "OnPremCluster" \
    --cluster-type connectedClusters \
    --name "gitops-config"

Arc Resource Bridge

# Deploy Arc Resource Bridge for VMware
az arcappliance create vmware \
    --resource-group "ArcVMware" \
    --name "arc-vmware-bridge" \
    --location "eastus" \
    --vcenter "vcenter.local" \
    --username "administrator@vsphere.local" \
    --config-file "arc-vmware-config.yaml"

# List VMware VMs managed through Arc
az connectedvmware vm list \
    --resource-group "ArcVMware"

# Enable Azure services on VMware VM
az connectedvmware vm guest-agent enable \
    --resource-group "ArcVMware" \
    --vm-name "webapp-vm" \
    --username "admin" \
    --password "password"

Best Practices

  1. Start with inventory - Connect all servers for visibility
  2. Apply policies gradually - Test before enforcing
  3. Use GitOps - Declarative configuration management
  4. Monitor centrally - Use Azure Monitor for unified view
  5. Plan connectivity - Consider direct vs. indirect modes

Azure Arc brings consistent management across your entire infrastructure footprint.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.