Back to Blog
5 min read

Multi-Cloud Management with Azure

Multi-cloud strategies allow organizations to leverage the best services from different cloud providers. Azure provides tools to manage resources across Azure, AWS, GCP, and on-premises environments from a single pane of glass.

Azure Arc for Multi-Cloud

Connecting AWS Resources

# Connect AWS EKS cluster to Azure Arc
# First, configure AWS credentials
aws configure

# Get EKS cluster credentials
aws eks update-kubeconfig --name my-eks-cluster --region us-east-1

# Connect to Azure Arc
az connectedk8s connect \
    --resource-group "MultiCloudK8s" \
    --name "aws-eks-cluster" \
    --location "eastus" \
    --distribution eks

# Verify connection
az connectedk8s show \
    --resource-group "MultiCloudK8s" \
    --name "aws-eks-cluster"

Connecting GCP Resources

# Connect GKE cluster to Azure Arc
# Configure GCP credentials
gcloud auth login
gcloud config set project my-gcp-project

# Get GKE cluster credentials
gcloud container clusters get-credentials my-gke-cluster --zone us-central1-a

# Connect to Azure Arc
az connectedk8s connect \
    --resource-group "MultiCloudK8s" \
    --name "gcp-gke-cluster" \
    --location "eastus" \
    --distribution gke

# List all connected clusters
az connectedk8s list --output table

Multi-Cloud Monitoring

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

public class MultiCloudMonitoringService
{
    private readonly LogsQueryClient _logsClient;

    public MultiCloudMonitoringService()
    {
        _logsClient = new LogsQueryClient(new DefaultAzureCredential());
    }

    public async Task<MultiCloudStatus> GetClusterStatusAsync(string workspaceId)
    {
        var query = @"
            KubeNodeInventory
            | summarize
                NodeCount = dcount(Computer),
                ReadyNodes = dcountif(Computer, Status == 'Ready'),
                LastHeartbeat = max(TimeGenerated)
                by ClusterName
            | extend
                CloudProvider = case(
                    ClusterName contains 'eks', 'AWS',
                    ClusterName contains 'gke', 'GCP',
                    ClusterName contains 'aks', 'Azure',
                    'Unknown'
                )
            | project ClusterName, CloudProvider, NodeCount, ReadyNodes, LastHeartbeat";

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

        var status = new MultiCloudStatus();

        foreach (var row in response.Value.Table.Rows)
        {
            status.Clusters.Add(new ClusterStatus
            {
                ClusterName = row["ClusterName"].ToString(),
                CloudProvider = row["CloudProvider"].ToString(),
                NodeCount = Convert.ToInt32(row["NodeCount"]),
                ReadyNodes = Convert.ToInt32(row["ReadyNodes"]),
                LastHeartbeat = (DateTime)row["LastHeartbeat"]
            });
        }

        return status;
    }

    public async Task<List<CostByCloud>> GetMultiCloudCostsAsync()
    {
        // Aggregate costs from multiple sources
        var costs = new List<CostByCloud>();

        // Azure costs from Cost Management API
        var azureCosts = await GetAzureCostsAsync();
        costs.Add(new CostByCloud { Provider = "Azure", Cost = azureCosts });

        // AWS costs (would need AWS Cost Explorer API)
        // GCP costs (would need GCP Billing API)

        return costs;
    }
}

public class MultiCloudStatus
{
    public List<ClusterStatus> Clusters { get; set; } = new();
}

public class ClusterStatus
{
    public string ClusterName { get; set; }
    public string CloudProvider { get; set; }
    public int NodeCount { get; set; }
    public int ReadyNodes { get; set; }
    public DateTime LastHeartbeat { get; set; }
}

Unified Policy Management

// Azure Policy for multi-cloud governance
{
    "mode": "All",
    "policyRule": {
        "if": {
            "allOf": [
                {
                    "field": "type",
                    "equals": "Microsoft.Kubernetes/connectedClusters"
                },
                {
                    "field": "Microsoft.Kubernetes/connectedClusters/distribution",
                    "in": ["eks", "gke", "aks"]
                },
                {
                    "field": "tags['CostCenter']",
                    "exists": false
                }
            ]
        },
        "then": {
            "effect": "deny"
        }
    },
    "parameters": {}
}

GitOps Across Clouds

# Flux configuration for multi-cloud GitOps
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
  name: multi-cloud-configs
  namespace: flux-system
spec:
  interval: 5m
  url: https://github.com/org/multi-cloud-k8s
  ref:
    branch: main
---
# Azure AKS configuration
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: azure-aks-apps
  namespace: flux-system
spec:
  interval: 10m
  path: ./clusters/azure-aks
  sourceRef:
    kind: GitRepository
    name: multi-cloud-configs
  prune: true
---
# AWS EKS configuration
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: aws-eks-apps
  namespace: flux-system
spec:
  interval: 10m
  path: ./clusters/aws-eks
  sourceRef:
    kind: GitRepository
    name: multi-cloud-configs
  prune: true
---
# GCP GKE configuration
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: gcp-gke-apps
  namespace: flux-system
spec:
  interval: 10m
  path: ./clusters/gcp-gke
  sourceRef:
    kind: GitRepository
    name: multi-cloud-configs
  prune: true

Terraform Multi-Cloud

# main.tf - Multi-cloud infrastructure
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
    }
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
    google = {
      source  = "hashicorp/google"
      version = "~> 4.0"
    }
  }
}

# Azure AKS
resource "azurerm_kubernetes_cluster" "aks" {
  name                = "multi-cloud-aks"
  location            = "eastus"
  resource_group_name = azurerm_resource_group.main.name
  dns_prefix          = "multicloud"

  default_node_pool {
    name       = "default"
    node_count = 3
    vm_size    = "Standard_D2_v2"
  }

  identity {
    type = "SystemAssigned"
  }

  tags = {
    Environment = "Production"
    ManagedBy   = "Terraform"
  }
}

# AWS EKS
resource "aws_eks_cluster" "eks" {
  name     = "multi-cloud-eks"
  role_arn = aws_iam_role.eks.arn

  vpc_config {
    subnet_ids = aws_subnet.eks[*].id
  }

  tags = {
    Environment = "Production"
    ManagedBy   = "Terraform"
  }
}

# GCP GKE
resource "google_container_cluster" "gke" {
  name     = "multi-cloud-gke"
  location = "us-central1"

  remove_default_node_pool = true
  initial_node_count       = 1

  labels = {
    environment = "production"
    managed-by  = "terraform"
  }
}

# Connect all to Azure Arc
resource "azurerm_arc_kubernetes_cluster" "eks" {
  name                = "aws-eks-cluster"
  resource_group_name = azurerm_resource_group.arc.name
  location            = "eastus"
  agent_public_key_certificate = file("eks-agent-cert.pem")

  identity {
    type = "SystemAssigned"
  }

  depends_on = [aws_eks_cluster.eks]
}

Cost Optimization

# Multi-cloud cost analysis
class MultiCloudCostAnalyzer:
    def __init__(self, azure_client, aws_client, gcp_client):
        self.azure = azure_client
        self.aws = aws_client
        self.gcp = gcp_client

    def get_total_costs(self, start_date, end_date):
        costs = {
            'azure': self.get_azure_costs(start_date, end_date),
            'aws': self.get_aws_costs(start_date, end_date),
            'gcp': self.get_gcp_costs(start_date, end_date)
        }

        total = sum(costs.values())
        percentages = {k: (v/total)*100 for k, v in costs.items()}

        return {
            'costs': costs,
            'total': total,
            'percentages': percentages
        }

    def get_optimization_recommendations(self):
        recommendations = []

        # Check for idle resources across clouds
        # Check for right-sizing opportunities
        # Check for reserved instance opportunities

        return recommendations

Best Practices

  1. Standardize on Kubernetes - Use K8s as the common platform
  2. Centralize monitoring - Azure Monitor for all clouds
  3. Unified policy - Azure Policy across Arc-enabled resources
  4. GitOps everywhere - Consistent deployment model
  5. Cost visibility - Aggregate costs for full picture

Multi-cloud management with Azure provides the flexibility to use best-of-breed services while maintaining operational consistency.

Michael John Peña

Michael John Peña

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