Module 02

Applications & Projects

Mastering the building blocks of ArgoCD deployments

Use arrow keys or click sides to navigate

The Challenge

The Multi-Team Problem

Your company has three teams: frontend, backend, and platform. Each team has their own Git repo and deploys to different namespaces. The frontend team should never accidentally deploy to the backend namespace. How do you enforce these boundaries while giving each team autonomy? This is where ArgoCD Applications and AppProjects shine.

Application Anatomy

apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: frontend-app # Unique name in ArgoCD namespace: argocd # Must be in argocd namespace labels: team: frontend env: production finalizers: - resources-finalizer.argocd.argoproj.io spec: project: frontend-team # AppProject reference source: ... # Where to find manifests destination: ... # Where to deploy syncPolicy: ... # How and when to sync

The metadata Section

The finalizer trick: Adding resources-finalizer.argocd.argoproj.io means deleting the Application also deletes all its deployed resources. Without it, resources are orphaned in the cluster.

The source Section

spec: source: repoURL: https://github.com/your-org/manifests.git targetRevision: main # Branch, tag, or commit SHA path: apps/frontend # Path within the repo

targetRevision

Can be a branch (main), tag (v1.2.3), full commit SHA, or HEAD.

path

Directory within the repo. ArgoCD auto-detects the tool (Helm, Kustomize, plain YAML).

Source Type: Directory (Plain YAML)

The simplest source type -- just plain Kubernetes manifests in a directory.

spec: source: repoURL: https://github.com/your-org/manifests.git targetRevision: main path: apps/frontend directory: recurse: true # Include subdirectories exclude: '{*.test.yaml}' # Glob exclude patterns include: '{*.yaml,*.yml}' # Glob include patterns

When to use: Small projects, learning ArgoCD, or when you prefer fully rendered YAML without templating overhead.

Source Type: Helm Charts

# Option A: Helm chart from a Helm repository spec: source: repoURL: https://charts.bitnami.com/bitnami chart: nginx targetRevision: 15.3.5 # Chart version helm: releaseName: my-nginx values: | replicaCount: 3 service: type: ClusterIP parameters: - name: image.tag value: "1.25.0"

ArgoCD runs helm template internally -- it does not use helm install. This means Helm hooks are handled differently (via ArgoCD resource hooks).

Helm: Values Files from Git

# Option B: Helm chart in a Git repo with value files spec: source: repoURL: https://github.com/your-org/charts.git targetRevision: main path: charts/my-app helm: releaseName: my-app valueFiles: - values.yaml - values-production.yaml # Overrides values.yaml parameters: - name: image.tag value: "v2.1.0" # Highest priority override

Priority order: parameters override valueFiles, which override the chart's default values.yaml. This is the same as Helm's native behavior.

Quiz Time

Source Types Check

1. When ArgoCD deploys a Helm chart, which Helm command does it use internally?

Correct! ArgoCD uses helm template to render manifests, then applies them with its own sync engine. It does not use Tiller or helm install.

2. What does the resources-finalizer.argocd.argoproj.io finalizer do?

Correct! The finalizer triggers a cascading delete -- when you delete the Application CRD, ArgoCD also deletes all the Kubernetes resources it manages.

3. In ArgoCD Helm source, which has the highest override priority?

Correct! Parameters have the highest priority, then valueFiles (in order), then the chart defaults. This matches Helm's native --set flag behavior.

Source Type: Kustomize

spec: source: repoURL: https://github.com/your-org/manifests.git targetRevision: main path: overlays/production # Directory with kustomization.yaml kustomize: namePrefix: prod- nameSuffix: -v2 images: - name: my-app newTag: v1.2.3 commonLabels: env: production commonAnnotations: managed-by: argocd

ArgoCD auto-detects Kustomize when it finds a kustomization.yaml file. It runs kustomize build internally.

Kustomize: Base + Overlays Pattern

base/
overlays/dev
overlays/staging
overlays/prod

Best practice: Create one ArgoCD Application per overlay. Point each to its overlay directory. The base contains shared manifests; overlays contain environment-specific patches.

The destination Section

spec: destination: # Option A: Deploy to the same cluster ArgoCD runs in server: https://kubernetes.default.svc namespace: frontend-prod # Option B: Deploy to a registered external cluster # server: https://aks-cluster-2.eastus.azmk8s.io # namespace: frontend-prod # Option C: Use cluster name instead of URL # name: production-cluster # namespace: frontend-prod

Registering External Clusters

# Add a cluster using the CLI (uses current kubeconfig context) argocd cluster add aks-production-context # List registered clusters argocd cluster list # Under the hood, ArgoCD creates a ServiceAccount in the # target cluster with appropriate RBAC permissions and stores # the credentials as a Kubernetes Secret in the argocd namespace.

AKS tip: For Azure Kubernetes Service, ensure the ArgoCD ServiceAccount has appropriate Azure RBAC or Kubernetes RBAC on the target cluster. Consider using Workload Identity for keyless auth.

The syncPolicy Section

spec: syncPolicy: automated: prune: true # Delete resources removed from Git selfHeal: true # Revert manual cluster changes allowEmpty: false # Don't sync if no resources found syncOptions: - CreateNamespace=true # Create namespace if missing - PrunePropagationPolicy=foreground - PruneLast=true # Prune after all syncs complete retry: limit: 5 backoff: duration: 5s factor: 2 maxDuration: 3m

We will dive deep into sync strategies in Module 03. For now, know that automated means ArgoCD syncs without human intervention.

Deep Dive

What is an AppProject?

An AppProject is a logical grouping and security boundary for ArgoCD Applications.

Think of AppProjects as tenants in a multi-team ArgoCD installation. Each project defines what sources an app can use, where it can deploy, and what resources it can manage.

Quiz Time

Destinations & Sync

1. What is the Kubernetes API server URL for the cluster ArgoCD is running in?

Correct! https://kubernetes.default.svc is the in-cluster Kubernetes API address, automatically available to any pod running in the cluster.

2. How does ArgoCD store credentials for external clusters?

Correct! Cluster credentials are stored as labeled Kubernetes Secrets in the argocd namespace. ArgoCD creates a ServiceAccount on the target cluster and stores the token.

3. What is the primary purpose of an AppProject?

Correct! AppProjects define security boundaries -- they restrict which repos, clusters, namespaces, and resource types an Application can use.

The default Project

apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: default namespace: argocd spec: sourceRepos: - '*' # Allow ALL repositories destinations: - namespace: '*' # Allow ALL namespaces server: '*' # Allow ALL clusters clusterResourceWhitelist: - group: '*' # Allow ALL cluster resources kind: '*'

Warning: The default project has no restrictions. In production, always create custom AppProjects with least-privilege access. Never let application teams use the default project.

Creating a Restricted AppProject

apiVersion: argoproj.io/v1alpha1 kind: AppProject metadata: name: frontend-team namespace: argocd spec: description: "Frontend team applications" sourceRepos: - 'https://github.com/your-org/frontend-*' - 'https://charts.bitnami.com/bitnami' destinations: - namespace: 'frontend-*' server: https://kubernetes.default.svc - namespace: 'frontend-*' server: https://aks-staging.eastus.azmk8s.io clusterResourceWhitelist: [] # No cluster-scoped resources namespaceResourceWhitelist: - group: 'apps' kind: 'Deployment' - group: '' kind: 'Service' - group: '' kind: 'ConfigMap' - group: '' kind: 'Secret' - group: 'networking.k8s.io' kind: 'Ingress'

AppProject: Source Restrictions

spec: # Only these repos can be used as Application sources sourceRepos: - 'https://github.com/your-org/frontend-manifests.git' - 'https://github.com/your-org/shared-charts.git' - 'https://charts.bitnami.com/bitnami' # Wildcards are supported # - 'https://github.com/your-org/*' # Any repo in org # - '*' # Any repo (not recommended)

Security benefit: Even if a developer has ArgoCD access, they cannot point an Application to a malicious external repo -- the AppProject blocks it.

AppProject: Destination Restrictions

spec: destinations: # Allow specific namespace on local cluster - namespace: frontend-prod server: https://kubernetes.default.svc # Wildcard namespace pattern - namespace: 'frontend-*' server: https://kubernetes.default.svc # Deny specific namespace (use deny list) # Note: Use namespaceResourceBlacklist for fine-grained control # Which cluster-scoped resources are allowed clusterResourceWhitelist: - group: '' kind: Namespace # Only allow Namespace creation # Or block specific cluster resources clusterResourceBlacklist: - group: '' kind: Node # Never allow Node modifications

AppProject: RBAC Roles

spec: roles: - name: frontend-developers description: "Frontend team access" policies: # Allow sync and get for apps in this project - p, proj:frontend-team:frontend-developers, applications, get, frontend-team/*, allow - p, proj:frontend-team:frontend-developers, applications, sync, frontend-team/*, allow # Deny delete - p, proj:frontend-team:frontend-developers, applications, delete, frontend-team/*, deny groups: # Map to SSO groups (Azure AD groups) - "azure-ad-frontend-team-group"

Multi-tenancy pattern: Each team gets their own AppProject with RBAC roles mapped to their Azure AD groups. Teams can sync their own apps but cannot touch other teams' resources.

AppProject: Sync Windows

spec: syncWindows: # Allow syncs only during business hours on weekdays - kind: allow schedule: '0 9 * * 1-5' # Cron: Mon-Fri at 9 AM duration: 8h # For 8 hours (9 AM - 5 PM) applications: - '*' namespaces: - 'frontend-prod' # Block syncs during a maintenance window - kind: deny schedule: '0 2 * * 6' # Sat at 2 AM duration: 4h applications: - '*' manualSync: true # Also blocks manual syncs

Sync windows let you control when deployments can happen. Perfect for production environments with change windows.

Quiz Time

AppProject Knowledge

1. Why is the "default" AppProject considered insecure for production?

Correct! The default project uses wildcards (*) for everything -- any repo, any cluster, any namespace, any resource. In production, create restricted projects per team.

2. What does clusterResourceWhitelist: [] (empty list) mean in an AppProject?

Correct! An empty whitelist means no cluster-scoped resources can be created by Applications in this project. Only namespace-scoped resources are permitted.

3. How can AppProject RBAC roles be integrated with Azure AD?

Correct! AppProject roles can map their groups field to Azure AD group names. When Dex or direct OIDC is configured for Azure AD, group claims are matched to project roles.
Credentials

Repository Credentials

ArgoCD needs credentials to clone private Git repositories and access private Helm chart repositories.

HTTPS + Token

argocd repo add \ https://github.com/org/repo.git \ --username git \ --password ghp_xxxxxxxxxxxx

SSH Key

argocd repo add \ [email protected]:org/repo.git \ --ssh-private-key-path \ ~/.ssh/id_rsa

Credential Templates

apiVersion: v1 kind: Secret metadata: name: github-org-creds namespace: argocd labels: argocd.argoproj.io/secret-type: repo-creds # Template! stringData: type: git url: https://github.com/your-org # Prefix match password: ghp_your_personal_access_token username: git

Credential templates use repo-creds label type (not repository). They match any repo URL that starts with the given prefix. Add one template and all repos under that org are covered automatically.

Helm Repository Credentials

# Add a private Helm repository argocd repo add https://charts.yourcompany.com \ --type helm \ --name internal-charts \ --username admin \ --password secret123 # For OCI-based Helm registries (ACR, ECR, etc.) argocd repo add myregistry.azurecr.io \ --type helm \ --name acr-charts \ --enable-oci \ --username 00000000-0000-0000-0000-000000000000 \ --password $(az acr login --name myregistry --expose-token \ --query accessToken -o tsv)

Azure Container Registry supports OCI Helm charts natively. Use a Service Principal or Managed Identity token.

Application Health Assessment

ArgoCD has built-in health checks for standard Kubernetes resource types.

Built-in Health Checks

  • Deployment -- checks rollout status
  • StatefulSet -- checks ready replicas
  • DaemonSet -- checks desired vs ready
  • Service -- checks endpoints
  • Ingress -- checks assigned address
  • PVC -- checks bound status
  • Pod -- checks phase and conditions

Health Statuses

  • Healthy -- working correctly
  • Progressing -- updating
  • Degraded -- failing
  • Suspended -- paused
  • Missing -- not found
  • Unknown -- no check

Sync Status In-Depth

Synced

Live = Desired. All is well.

OutOfSync

Live differs from Git. Needs sync.

Unknown

Cannot determine state.

Important distinction: Sync status and health status are independent. An app can be Synced but Degraded (the manifests match Git, but the app is crashing) or OutOfSync but Healthy (someone manually scaled up replicas).

Viewing Apps with CLI

# List all applications with their sync/health status argocd app list # Detailed view of a specific application argocd app get frontend-prod # Output includes: # - Sync status and health status # - Source repo, revision, path # - Destination cluster and namespace # - List of managed resources with individual status # - Last sync result and any errors # Filter by project argocd app list --project frontend-team # Get application manifests (what ArgoCD would apply) argocd app manifests frontend-prod

Application Diffing

# View the diff between desired (Git) and live (cluster) state argocd app diff frontend-prod # The diff shows exactly what would change on sync # - Added lines (resources in Git but not in cluster) # - Removed lines (resources in cluster but not in Git) # - Modified lines (resources that differ) # Ignore specific fields in diff (e.g., auto-generated) spec: ignoreDifferences: - group: apps kind: Deployment jsonPointers: - /spec/replicas # Ignore HPA-managed replicas - group: '*' kind: '*' managedFieldsManagers: - kube-controller-manager # Ignore controller changes
Quiz Time

Final Review

1. What is the label type for ArgoCD credential templates (matching multiple repos by URL prefix)?

Correct! The repo-creds type creates a credential template that matches any repository URL starting with the given prefix. The repository type is for individual repo credentials.

2. Can an application be "Synced" and "Degraded" at the same time?

Correct! Sync status and health status are independent. "Synced" means the live manifests match Git. "Degraded" means the resources are not working. Both can be true simultaneously.

3. What does ignoreDifferences with /spec/replicas help solve?

Correct! When HPA manages replicas, the live count may differ from Git. Using ignoreDifferences on /spec/replicas prevents ArgoCD from showing the app as OutOfSync due to HPA scaling.
Summary

Module 02 Recap

Next up: Module 03 -- Sync Strategies and Hooks

Module 02 Complete

You can now build Applications and lock them down with Projects

Continue to Module 03 for sync strategies and resource hooks

← Back