Private/Save-HelmBackup.ps1
function Save-HelmBackup { param ( [string]$Namespace, [string]$OutputPath, [switch]$DryRun, [switch]$Verbose, [switch]$AllNamespaces, [switch]$AllNonSystemNamespaces, [switch]$SnapshotHelm, # Perform a full Helm snapshot (includes backend storage) [switch]$SnapshotHelmUsedValues # Perform only Helm used values snapshot ) # Set the verbose preference based on the switch if ($Verbose) { $VerbosePreference = "Continue" } # Check if Helm is installed if (!(Get-Command "helm" -ErrorAction SilentlyContinue)) { Write-Host "'helm' is not installed or not found in your system's PATH." -ForegroundColor Red Write-Host "Please install 'helm' before running Save-HelmBackup." -ForegroundColor Red Write-Host "You can install 'helm' from: https://helm.sh/docs/intro/install/" -ForegroundColor Yellow return } # Check if kubectl is installed if (!(Get-Command "kubectl" -ErrorAction SilentlyContinue)) { Write-Host "'kubectl' is not installed or not found in your system's PATH." -ForegroundColor Red Write-Host "Please install 'kubectl' before running Save-HelmBackup." -ForegroundColor Red Write-Host "You can install 'kubectl' from: https://kubernetes.io/docs/tasks/tools/install-kubectl/" -ForegroundColor Yellow return } # Function to run Helm command and return output function Invoke-HelmCommand { param ( [string]$command # Helm command without the 'helm' part ) $processInfo = New-Object System.Diagnostics.ProcessStartInfo $processInfo.FileName = "helm" $processInfo.Arguments = $command $processInfo.RedirectStandardOutput = $true $processInfo.RedirectStandardError = $true $processInfo.UseShellExecute = $false $processInfo.CreateNoWindow = $true $process = New-Object System.Diagnostics.Process $process.StartInfo = $processInfo # Start the Helm process $process.Start() | Out-Null # Capture output and error $output = $process.StandardOutput.ReadToEnd() $stderr = $process.StandardError.ReadToEnd() $process.WaitForExit() if ($stderr) { Write-Host "Error running Helm command: $stderr" -ForegroundColor Red } return $output } # Function to fetch used values function Get-HelmUsedValues { param ( [string]$ReleaseName, [string]$NamespaceOption ) $helmUsedValuesCmd = "get values $ReleaseName $NamespaceOption -o yaml" Write-Verbose "Running command: helm $helmUsedValuesCmd" $usedValuesOutput = Invoke-HelmCommand $helmUsedValuesCmd if ($usedValuesOutput) { return $usedValuesOutput } else { Write-Host "No used values found for release '$ReleaseName'." -ForegroundColor Yellow return $null } } # Function to backup backend storage function Backup-HelmBackend { param ( [string]$Namespace ) Write-Verbose "Backing up Helm storage backend for namespace: $Namespace" $secrets = kubectl get secrets -n $Namespace -o json | ConvertFrom-Json $backendType = $secrets.items | Where-Object { $_.type -eq 'helm.sh/release.v1' -and $_.metadata.name -like '*sh.helm.release.v1*' } | Select-Object -First 1 if ($backendType -match "helm.sh/release.v1") { Write-Verbose "Detected backend: Secrets" $releases = kubectl get secrets -n $Namespace -l "owner=helm" -o json | ConvertFrom-Json } else { Write-Verbose "Detected backend: ConfigMaps" $releases = kubectl get configmaps -n $Namespace -l "owner=helm" -o json | ConvertFrom-Json } # Prepare YAML output file $releaseName = $release.metadata.labels.name $safeName = $releaseName -replace "[:\\/]", "_" $timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss" $filePath = Join-Path -Path $OutputPath -ChildPath "${safeName}_backend_$timestamp.yaml" New-Item -Path $filePath -ItemType File -Force | Out-Null # Convert and append each release to the YAML file foreach ($release in $releases.items) { $yamlContent = ConvertTo-Yaml -Data $release Add-Content -Path $filePath -Value $yamlContent Add-Content -Path $filePath -Value "---" } Write-Host "Helm release '$releaseName' backend saved: $filePath" -ForegroundColor Green } # Function to process a namespace function Backup-HelmNamespace { param ( [string]$Namespace ) $namespaceOption = "" if ($Namespace) { $namespaceOption = "-n $Namespace" } # Back up backend storage if SnapshotHelm is enabled if ($SnapshotHelm) { Backup-HelmBackend -Namespace $Namespace } $helmListCmd = "list $namespaceOption --output json" Write-Verbose "Running command: helm $helmListCmd" $releasesOutput = Invoke-HelmCommand $helmListCmd if ($releasesOutput) { $releases = $releasesOutput | ConvertFrom-Json if ($releases.Count -eq 0) { Write-Host "No Helm releases found in the namespace '$Namespace'." -ForegroundColor Yellow return } foreach ($release in $releases) { $releaseName = $release.name $releaseNamespace = $release.namespace $timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss" if ($DryRun) { Write-Host "Dry run: Found Helm release '$releaseName' in namespace '$releaseNamespace'." } else { # Full Helm snapshot (values, and manifest) if ($SnapshotHelm) { # Fetch values $helmGetValuesCmd = "get values $releaseName $namespaceOption -o yaml" Write-Verbose "Running command: helm $helmGetValuesCmd" $valuesOutput = Invoke-HelmCommand $helmGetValuesCmd if ($valuesOutput) { $safeReleaseName = $releaseName -replace "[:\\/]", "_" $valuesFile = Join-Path -Path $OutputPath -ChildPath "${safeReleaseName}_values_$timestamp.yaml" $valuesOutput | Out-File -FilePath $valuesFile -Force Write-Host "Helm release '$releaseName' values saved: $valuesFile" -ForegroundColor Green } # Fetch manifest $helmGetManifestCmd = "get manifest $releaseName $namespaceOption" Write-Verbose "Running command: helm $helmGetManifestCmd" $manifestOutput = Invoke-HelmCommand $helmGetManifestCmd if ($manifestOutput) { $manifestFile = Join-Path -Path $OutputPath -ChildPath "${safeReleaseName}_manifest_$timestamp.yaml" $manifestOutput | Out-File -FilePath $manifestFile -Force Write-Host "Helm release '$releaseName' manifest saved: $manifestFile" -ForegroundColor Green } } # Only Helm used values snapshot if ($SnapshotHelmUsedValues) { $usedValuesOutput = Get-HelmUsedValues -ReleaseName $releaseName -NamespaceOption $namespaceOption if ($usedValuesOutput) { $safeReleaseName = $releaseName -replace "[:\\/]", "_" $usedValuesFile = Join-Path -Path $OutputPath -ChildPath "${safeReleaseName}_used_values_$timestamp.yaml" $usedValuesOutput | Out-File -FilePath $usedValuesFile -Force Write-Host "Helm release '$releaseName' used values saved: $usedValuesFile" -ForegroundColor Green } } } } } } try { # Determine the namespace option if ($AllNamespaces) { $namespaces = kubectl get namespaces -o json | ConvertFrom-Json $allClusterNamespaces = $namespaces.items | ForEach-Object { $_.metadata.name } } elseif ($AllNonSystemNamespaces) { $namespaces = kubectl get namespaces -o json | ConvertFrom-Json $allClusterNamespaces = $namespaces.items | Where-Object { $_.metadata.name -notmatch "^(kube-system|kube-public|kube-node-lease|default)$" } | ForEach-Object { $_.metadata.name } } elseif ($Namespace) { $allClusterNamespaces = @($Namespace) } else { Write-Host "No namespace specified." -ForegroundColor Red return } # Process each namespace $allClusterNamespaces | ForEach-Object { Write-Host "Processing namespace: $_" Backup-HelmNamespace -Namespace $_ } } catch { Write-Host "Error occurred while backing up Helm releases: $_" -ForegroundColor Red } } |