# Appendix H - Velero Installation Guide

This guide explains how to install Velero on an EKS cluster using only the AWS plugin (without CSIsnapshots).&#x20;

* Installation approaches: Helm or Terraform.&#x20;
* Authentication options: IRSA or EKS Pod Identity.&#x20;

### Prerequisites

* **EKS Cluster Requirements**&#x20;
  * **EKS cluster** with either IRSA or Pod Identity enabled&#x20;
  * **Kubernetes version** v1.16+
* **AWS Resources**&#x20;
  * **S3 bucket** for storing backups&#x20;
  * **IAM role** with necessary permissions&#x20;
  * **AWS CLI** configured with appropriate credentials
* **Tools**&#x20;
  * **kubectl** configured to access your cluster&#x20;
  * **Helm** for Helm approach&#x20;
  * **Terraform** for Terraform approach&#x20;

### Authentication Methods&#x20;

**Option 1: IRSA (IAM Roles for Service Accounts)**&#x20;

* Uses OIDC provider and IAM roles&#x20;
* More secure, recommended for production&#x20;
* Requires OIDC provider configuration&#x20;

**Option 2: EKS Pod Identity (pod identity agent)**&#x20;

* Simpler setup (no OIDC provider; no SSO/Identity Center involved)&#x20;
* Requires the EKS Pod Identity Agent addon&#x20;
* Trust policy principal: `Service: pods.eks.amazonaws.com`&#x20;
* Create a Pod Identity Association mapping `<namespace>/<serviceAccount>` → IAM role

### Approach 1: Helm Installation&#x20;

#### **Step 1**: Create S3 Bucket&#x20;

Create an S3 bucket through the AWS Console or CLI:

```
aws s3 mb s3://your-velero-backup-bucket --region us-east-1
aws s3api put-bucket-versioning --bucket your-velero-backup-bucket --versioning-configuration Status=Enabled 
```

#### **Step 2**: Create IAM Role and Policy&#x20;

**Required IAM Permissions**&#x20;

Velero needs specific AWS permissions to perform backups and restores. When creating the IAM policy in the AWS Console, include these permissions:&#x20;

**S3 Permissions** (scope to your Velero bucket only):&#x20;

* s3:ListBucket - on arn:aws:s3:::your-velero-backup-bucket&#x20;
* s3:GetObject - on arn:aws:s3:::your-velero-backup-bucket/\*&#x20;
* s3:PutObject  - on arn:aws:s3:::your-velero-backup-bucket/\*&#x20;
* s3:DeleteObject  - on arn:aws:s3:::your-velero-backup-bucket/\*&#x20;

**EC2 Permissions** (for EBS snapshot management, resource: \*):&#x20;

* ec2:DescribeVolumes&#x20;
* ec2:CreateSnapshot&#x20;
* ec2:DeleteSnapshot&#x20;
* ec2:DescribeSnapshots&#x20;
* ec2:CreateTags&#x20;
* ec2:CreateVolume&#x20;
* ec2:AttachVolume&#x20;
* ec2:DetachVolume&#x20;
* ec2:DescribeAvailabilityZones&#x20;
* ec2:DescribeTags&#x20;

**Understanding Service Accounts and IRSA**&#x20;

**What is IRSA?**&#x20;

IRSA (IAM Roles for Service Accounts) allows Kubernetes pods to securely use AWS services without storing static credentials. It's the recommended approach for EKS workloads.&#x20;

**How it works:**&#x20;

1. Your EKS cluster has an OIDC provider that acts as an identity issuer&#x20;
2. You create an IAM role that trusts this OIDC provider&#x20;
3. Velero's Kubernetes ServiceAccount ( `velero-server` ) gets annotated with the IAM role ARN&#x20;
4. When Velero pods run, they automatically receive temporary AWS credentials by assuming the IAM role&#x20;
5. No AWS access keys are stored in your cluster - credentials are temporary and auto-rotate&#x20;

**Creating the IAM Role (AWS Console)**&#x20;

**Step 2.1: Create IAM Policy**&#x20;

1. Go to AWS Console → IAM → Policies → Create Policy&#x20;
2. Click JSON tab and paste a policy with the permissions listed above&#x20;
3. Name it `VeleroBackupPolicy`&#x20;
4. Create the policy&#x20;

**Step 2.2: Create IAM Role with OIDC Trust**&#x20;

1. Go to IAM → Roles → Create Role&#x20;
2. Select "Web Identity" as trusted entity type&#x20;
3. Choose your EKS cluster's OIDC provider from the dropdown&#x20;
4. For "Audience", select `sts.amazonaws.com`&#x20;
5. Add a condition:&#x20;
   1. Condition: `StringEquals`&#x20;
   2. Key: `<your-oidc-provider>:sub`&#x20;
   3. Value: `system:serviceaccount:velero:velero-server`&#x20;
6. Attach the VeleroBackupPolicy you created&#x20;
7. Name the role (e.g., `VeleroIRSARole` )&#x20;
8. Copy the Role ARN - you'll need it for Helm installation&#x20;

{% hint style="info" %}
The OIDC provider URL can be found in EKS Console → Your Cluster → Overview → OpenIDConnect provider URL&#x20;
{% endhint %}

**Step 3: Install Velero with Helm**&#x20;

Replace \<YOUR\_BUCKET> , \<YOUR\_REGION> , and \<IAM\_ROLE\_ARN> with your actual values.

\# Add Helm repository

```
helm repo add vmware-tanzu https://vmware-tanzu.github.io/helm-charts
helm repo update
```

\# Install Velero with command-line flags

```
helm install velero vmware-tanzu/velero \
 --namespace velero \
 --create-namespace \
 --set configuration.backupStorageLocation[0].name=default \
 --set configuration.backupStorageLocation[0].provider=aws \
 --set configuration.backupStorageLocation[0].bucket=<YOUR_BUCKET> \
 --set configuration.backupStorageLocation[0].config.region=<YOUR_REGION> \
 --set configuration.volumeSnapshotLocation[0].name=default \ 
 --set configuration.volumeSnapshotLocation[0].provider=aws \
 --set configuration.volumeSnapshotLocation[0].config.region=<YOUR_REGION> \
 --set initContainers[0].name=velero-plugin-for-aws \ 
 --set initContainers[0].image=velero/velero-plugin-for-aws:v1.10.0 \ 
 --set initContainers[0].volumeMounts[0].mountPath=/target \ 
 --set initContainers[0].volumeMounts[0].name=plugins \ 
 --set credentials.useSecret=false \ 
 --set serviceAccount.server.create=true \ 
 --set serviceAccount.server.name=velero-server \ 
 --set serviceAccount.server.annotations."eks.amazonaws.com/role-arn"=<IAM_ROLE_ARN> \ 
 --set deployNodeAgent=true \
--set uploaderType=kopia
```

**Step 4: Verify Installation**

\#  Check pods are running

`kubectl get pods -n velero`

\# Verify ServiceAccount annotation

`kubectl get serviceaccount velero-server -n velero -o yaml`

\# Check Velero can authenticate to AWS

`kubectl exec -n velero deployment/velero -- aws sts get-caller-identity`

\# Check backup storage location

`kubectl get backupstoragelocation -n velero`

**Step 5: Install Velero CL**I&#x20;

Download the Velero CLI to manage backups from your machine:

\# Linux

```
wget https://github.com/vmware-tanzu/velero/releases/download/v1.14.0/velero-v1.14.0-linux-amd64.tar.gz
tar -xvf velero-v1.14.0-linux-amd64.tar.gz
sudo mv velero-v1.14.0-linux-amd64/velero /usr/local/bin/
```

\# macOS

`brew install velero`

\# Verify

`velero version`&#x20;

#### `Step 6: Test Backup`

\#Create a test backup

`velero backup create test-backup --include-namespaces default`

\# Check status

`velero backup describe test-backup`

\# List backups

`velero backup get`

### Approach 2: Terraform Bootstrapping&#x20;

#### 2.1 Choose authentication (IRSA or EKS Pod Identity)&#x20;

**IRSA (existing/standard)**&#x20;

* Ensure an EKS OIDC provider exists.&#x20;
* Create an IAM role with trust for that OIDC provider and `system:serviceaccount::<ns>:<sa>` .&#x20;
* Annotate the ServiceAccount with the role ARN:&#x20;
  * `serviceAccount.server.annotations."eks.amazonaws.com/role-arn" = <role-arn>`
  * Use the key exactly `eks.amazonaws.com/role-arn` (case-sensitive)

**EKS Pod Identity**&#x20;

* Install addon: `eks-pod-identity-agent` .&#x20;
* Create an IAM role with trust principal `Service: pods.eks.amazonaws.com` .&#x20;
* Create a Pod Identity Association mapping `<namespace> /<serviceAccount>` → role.&#x20;
* Remove any IRSA annotations from the SA.&#x20;

#### 2.2 Minimal Terraform for Velero (AWS plugin by default)&#x20;

```
resource "helm_release" "velero" {
 name = "velero" 
 repository = "https://vmware-tanzu.github.io/helm-charts" 
 chart = "velero" 
 version = "10.0.11" 
 namespace = "velero" 
 create_namespace = true 
 
values = [yamlencode({ 
 configuration = {
  # Default: AWS plugin only (no CSI) 
  features = ""
  backupStorageLocation = [{
   name = "default"
   provider = "aws" 
   bucket = "" 
   config = { region = "" }
  }] 
  # Enable CSI snapshots (optional): 
  # features = "EnableCSI" 
  # volumeSnapshotLocation = [{ name = "default", provider = "aws", config = { region = "" } }] 
  # extraArgs = ["--default-snapshot-move-data=true"] # Only applies to CSI data-mover flows 
 } 
 
 # Plugins 
 plugins = ["velero/velero-plugin-for-aws:v1.12.0"] 
 # FS backups (optional but recommended to have available) 
 deployNodeAgent = true 
 uploaderType = "kopia" 
 
 # IRSA: annotate SA with the role (if using IRSA) 
 # serviceAccount = { server = { create = true, name = "velero-server", annotations = { "eks.amazonaws.com/role-arn" = var.velero_role_arn } } } 
 })]
} 
```

#### 2.3 CSI prerequisites (only if enabling CSI)&#x20;

* Install the snapshot-controller (e.g., Piraeus `snapshot-controller` chart) and CRDs.&#x20;
* Create an EBS CSI `VolumeSnapshotClass` ( `driver: ebs.csi.aws.com` ).&#x20;
* Label the VSC so Velero detects it: `velero.io/csi-volumesnapshot-class=true` .&#x20;

{% hint style="info" %}
Tip: Run snapshot-controller with `replicaCount: 2` .&#x20;
{% endhint %}

#### 2.4 Deploy&#x20;

```
terraform init
terraform apply -auto-approve
```

#### 2.5 Verify&#x20;

```
kubectl -n velero get deploy velero -o 
jsonpath='{.spec.template.spec.containers[0].args}'; echo
kubectl -n velero logs deploy/velero | grep -Ei 'velero-plugin|credentials|error' || true 
```

### Verification&#x20;

#### **Check Installation**

\# Check Velero pods

`kubectl get pods -n velero`

\# Test backup

`kubectl exec -n velero deployment/velero -- velero backup create test-backup --include-namespaces default`

### **Required IAM Permissions**&#x20;

**S3**: List, Get, Put, Delete objects in backup bucket&#x20;

**EC2**: Describe volumes, create/delete snapshots, create tags&#x20;

### **Troubleshooting**&#x20;

**Common Issues**&#x20;

1. **Authentication not working**: Check IRSA or Pod Identity configuration&#x20;
2. **S3 access denied**: Verify IAM role permissions&#x20;
3. **Plugin not found**: Check plugin image

#### Debug Commands

\# Check Velero logs

`kubectl logs -n velero deployment/velero`

\# Test AWS credentials

`kubectl exec -n velero deployment/velero -- aws sts get-caller-identity`

### Security Considerations&#x20;

1. **Least privilege**: Only grant necessary permissions
2. **Bucket encryption**: Enable S3 bucket encryption&#x20;
3. **Access logging**: Enable S3 access logging&#x20;

### Cost Optimization&#x20;

1. **Lifecycle policies**: Set S3 lifecycle rules&#x20;
2. **Snapshot retention**: Configure deletion policies&#x20;
3. **Storage classes**: Use appropriate S3 storage classes&#x20;

#### Comparison: Installation Methods&#x20;

| Aspect         | Helm           | Terraform         |
| -------------- | -------------- | ----------------- |
| Complexity     | Simple         | Medium            |
| Infrastructure | Manual         | Automated         |
| Multi-cluster  | Manual         | Easy to replicate |
| Best for       | Single cluster | Multiple clusters |

### Support&#x20;

For issues with this setup:&#x20;

1. Check Velero documentation: [https://velero.io/docs/ ](https://velero.io/docs/)
2. Review AWS EKS documentation: <https://docs.aws.amazon.com/eks/>
3. Check Velero GitHub issues: <https://github.com/vmware-tanzu/velero/issues>

### Additional Notes&#x20;

* To enable CSI snapshots, follow Section 2.3 (controller, VSC, label) and set `configuration.features = "EnableCSI"` in Terraform.&#x20;
* For file-system backups, enable `deployNodeAgent = true` and `uploaderType = "kopia"` ; opt-in per PVC with `velero.io/fs-backup=true` .&#x20;
* If not on AWS, use the cloud plugin for your provider instead of the AWS plugin:&#x20;
  * GCP: `velero/velero-plugin-for-gcp`&#x20;
  * Azure: `velero/velero-plugin-for-microsoft-azure`&#x20;
