Azure Arc GitOps Integration with ArgoCD
Comprehensive guide for deploying and managing ArgoCD via Azure Arc-enabled Kubernetes and Azure Kubernetes Service (AKS) using Azure's managed GitOps extension.
Overview
Azure provides a managed ArgoCD experience through the Microsoft.ArgoCD cluster extension. This enables GitOps workflows on:
- Azure Arc-enabled Kubernetes: On-premises, multi-cloud, or edge Kubernetes clusters connected to Azure
- Azure Kubernetes Service (AKS): Azure's managed Kubernetes offering
Key Benefits
| Benefit | Description |
|---|---|
| Managed Installation | Azure handles ArgoCD deployment and upgrades |
| Workload Identity | Native Azure AD integration without managing secrets |
| Multi-Cluster | Consistent GitOps across hybrid environments |
| Azure Integration | Works with Azure Key Vault, ACR, and Azure AD |
| High Availability | Built-in HA mode with 3-node support |
Prerequisites
Azure Arc-enabled Kubernetes
Kubernetes cluster connected to Azure Arc:
# Connect cluster to Azure Arc az connectedk8s connect --name <cluster-name> \ --resource-group <resource-group>Required permissions:
Microsoft.Kubernetes/connectedClusters(read/write)Microsoft.KubernetesConfiguration/extensions(read/write)
Azure Kubernetes Service (AKS)
MSI-based AKS cluster (not SPN):
# Create MSI-based AKS cluster az aks create --resource-group <rg> --name <cluster> \ --enable-managed-identity # Convert existing SPN cluster to MSI az aks update -g <rg> -n <cluster> --enable-managed-identityRequired permissions:
Microsoft.ContainerService/managedClusters(read/write)Microsoft.KubernetesConfiguration/extensions(read/write)
Common Requirements
# Register Azure providers
az provider register --namespace Microsoft.Kubernetes
az provider register --namespace Microsoft.ContainerService
az provider register --namespace Microsoft.KubernetesConfiguration
# Install CLI extensions
az extension add -n k8s-configuration
az extension add -n k8s-extension
# Verify registration (wait for 'Registered' state)
az provider show -n Microsoft.KubernetesConfiguration -o table
Network Requirements
The GitOps agents require outbound access to:
| Endpoint | Purpose |
|---|---|
management.azure.com |
Azure Resource Manager communication |
<region>.dp.kubernetesconfiguration.azure.com |
Configuration data plane |
login.microsoftonline.com |
Azure AD token refresh |
mcr.microsoft.com |
Container image pulls |
| Git repository (port 22 or 443) | Source code sync |
Installation Methods
Method 1: Simple Installation (Single Node)
For development or single-node clusters:
az k8s-extension create \
--resource-group <resource-group> \
--cluster-name <cluster-name> \
--cluster-type managedClusters \
--name argocd \
--extension-type Microsoft.ArgoCD \
--release-train preview \
--config deployWithHighAvailability=false \
--config namespaceInstall=false \
--config "config-maps.argocd-cmd-params-cm.data.application\.namespaces=namespace1,namespace2"
Parameters:
| Parameter | Description |
|---|---|
deployWithHighAvailability=false |
Single-node deployment |
namespaceInstall=false |
Cluster-wide ArgoCD access |
application.namespaces |
Namespaces where ArgoCD can detect Applications |
Method 2: High Availability Installation (Production)
For production with 3+ nodes:
az k8s-extension create \
--resource-group <resource-group> \
--cluster-name <cluster-name> \
--cluster-type managedClusters \
--name argocd \
--extension-type Microsoft.ArgoCD \
--release-train preview \
--config namespaceInstall=false \
--config "config-maps.argocd-cmd-params-cm.data.application\.namespaces=default,argocd"
Method 3: Namespace-Scoped Installation
For multi-tenant clusters with isolated ArgoCD instances:
az k8s-extension create \
--resource-group <resource-group> \
--cluster-name <cluster-name> \
--cluster-type managedClusters \
--name argocd-team-a \
--extension-type Microsoft.ArgoCD \
--release-train preview \
--config namespaceInstall=true \
--target-namespace team-a-argocd
Workload Identity Integration (Recommended for Production)
Workload identity enables Azure AD authentication without managing secrets.
Bicep Template
var clusterName = '<aks-or-arc-cluster-name>'
var workloadIdentityClientId = '<managed-identity-client-id>'
var ssoWorkloadIdentityClientId = '<sso-managed-identity-client-id>'
var url = 'https://<public-ip-for-argocd-ui>/'
var oidcConfig = '''
name: Azure
issuer: https://login.microsoftonline.com/<your-tenant-id>/v2.0
clientID: <sso-client-id>
azure:
useWorkloadIdentity: true
requestedIDTokenClaims:
groups:
essential: true
requestedScopes:
- openid
- profile
- email
'''
var defaultPolicy = 'role:readonly'
var policy = '''
p, role:org-admin, applications, *, */*, allow
p, role:org-admin, clusters, get, *, allow
p, role:org-admin, repositories, get, *, allow
p, role:org-admin, repositories, create, *, allow
p, role:org-admin, repositories, update, *, allow
p, role:org-admin, repositories, delete, *, allow
g, <entra-group-id>, role:org-admin
'''
resource cluster 'Microsoft.ContainerService/managedClusters@2024-10-01' existing = {
name: clusterName
}
resource extension 'Microsoft.KubernetesConfiguration/extensions@2023-05-01' = {
name: 'argocd'
scope: cluster
properties: {
extensionType: 'Microsoft.ArgoCD'
releaseTrain: 'preview'
configurationSettings: {
'workloadIdentity.enable': 'true'
'workloadIdentity.clientId': workloadIdentityClientId
'workloadIdentity.entraSSOClientId': ssoWorkloadIdentityClientId
'config-maps.argocd-cm.data.oidc\\.config': oidcConfig
'config-maps.argocd-cm.data.url': url
'config-maps.argocd-rbac-cm.data.policy\\.default': defaultPolicy
'config-maps.argocd-rbac-cm.data.policy\\.csv': policy
'config-maps.argocd-cmd-params-cm.data.application\\.namespaces': 'default, argocd'
}
}
}
Deploy with Bicep
az deployment group create \
--resource-group <resource-group> \
--template-file argocd-extension.bicep
Setup Workload Identity Credentials
Retrieve OIDC issuer URL:
# For AKS az aks show -n <cluster> -g <rg> --query "oidcIssuerProfile.issuerUrl" -o tsv # For Arc-enabled Kubernetes az connectedk8s show -n <cluster> -g <rg> --query "oidcIssuerProfile.issuerUrl" -o tsvCreate managed identity:
az identity create --name argocd-identity --resource-group <rg>Create federated credential:
az identity federated-credential create \ --name argocd-federated \ --identity-name argocd-identity \ --resource-group <rg> \ --issuer <oidc-issuer-url> \ --subject system:serviceaccount:argocd:source-controller \ --audience api://AzureADTokenExchangeGrant ACR permissions (if using Azure Container Registry):
# For ABAC-enabled registries az role assignment create \ --role "Container Registry Repository Reader" \ --assignee <identity-client-id> \ --scope /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.ContainerRegistry/registries/<acr> # For non-ABAC registries az role assignment create \ --role "AcrPull" \ --assignee <identity-client-id> \ --scope /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.ContainerRegistry/registries/<acr>
Accessing ArgoCD UI
Option 1: LoadBalancer Service
kubectl -n argocd expose service argocd-server \
--type LoadBalancer \
--name argocd-server-lb \
--port 80 \
--target-port 8080
Option 2: Ingress Controller
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server
namespace: argocd
annotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: nginx
rules:
- host: argocd.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 443
Option 3: Port Forward (Development)
kubectl port-forward svc/argocd-server -n argocd 8080:443
Deploying Applications
Example: AKS Store Demo
kubectl apply -f - <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: aks-store-demo
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/Azure-Samples/aks-store-demo.git
targetRevision: HEAD
path: kustomize/overlays/dev
syncPolicy:
automated: {}
destination:
namespace: pets
server: https://kubernetes.default.svc
EOF
Multi-Source with Azure Container Registry
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
sources:
# Helm chart from ACR
- repoURL: <acr-name>.azurecr.io/helm
chart: myapp
targetRevision: 1.0.0
helm:
valueFiles:
- $values/overlays/prod/values.yaml
# Values from Git
- repoURL: https://github.com/org/config.git
targetRevision: main
ref: values
destination:
server: https://kubernetes.default.svc
namespace: myapp
Updating Configuration
Update ArgoCD configmaps through the extension (not directly via kubectl):
az k8s-extension update \
--resource-group <resource-group> \
--cluster-name <cluster-name> \
--cluster-type managedClusters \
--name argocd \
--config "config-maps.argocd-cm.data.url=https://<new-public-ip>/auth/callback"
Connecting to Private ACR
For private Azure Container Registry access with workload identity:
Use workload identity (configured above)
Add repository in ArgoCD:
argocd repo add <acr-name>.azurecr.io \ --type helm \ --name azure-acr \ --enable-oci
Deleting the Extension
az k8s-extension delete \
-g <resource-group> \
-c <cluster-name> \
-n argocd \
-t managedClusters \
--yes
Comparison: Azure Extension vs Manual Installation
| Aspect | Azure Extension | Manual Installation |
|---|---|---|
| Installation | az k8s-extension create |
kubectl apply or Helm |
| Upgrades | Managed by Azure | Manual |
| Workload Identity | Built-in support | Manual configuration |
| Azure AD SSO | Simplified setup | Complex OIDC config |
| Support | Azure support included | Community support |
| Customization | Limited to extension params | Full control |
| Multi-cluster | Centralized Azure management | Per-cluster management |
Troubleshooting
Extension Installation Failed
# Check extension status
az k8s-extension show \
-g <rg> -c <cluster> -t managedClusters \
-n argocd
# Check ArgoCD pods
kubectl get pods -n argocd
# Check extension operator logs
kubectl logs -n azure-arc -l app.kubernetes.io/component=extension-manager
Workload Identity Issues
# Verify federated credential
az identity federated-credential list \
--identity-name argocd-identity \
--resource-group <rg>
# Check service account annotation
kubectl get sa -n argocd source-controller -o yaml
Sync Failures
# Check ArgoCD application status
argocd app get <app-name>
# Check repo server logs
kubectl logs -n argocd -l app.kubernetes.io/name=argocd-repo-server
Best Practices for Azure GitOps
- Use Workload Identity: Avoid storing secrets; use Azure AD authentication
- Private Endpoints: Use Azure Private Link for ACR and Key Vault
- Azure Policy: Enforce GitOps compliance with Azure Policy
- Azure Monitor: Integrate ArgoCD metrics with Azure Monitor
- Separate Environments: Use different resource groups for dev/staging/prod
- RBAC: Map Azure AD groups to ArgoCD roles