🔍 Overview & Problem Statement
Expanding a VMDK disk in a vSphere environment traditionally involves logging into vCenter, locating the correct VM, identifying the right virtual disk (without confusing identically-sized disks), checking datastore free space, performing the resize, and then verifying the result inside the guest OS — all while following a change request and producing evidence for audit.
This is repetitive, error-prone, and time-consuming — especially when the same operation must be performed in both a Production and a DR vCenter. This blog walks through a PowerCLI automation script that plugs into a Jenkins Parameterised Build, turning a multi-step manual task into a single-click, fully logged, self-documenting job.
🏗️ Solution Architecture
The Jenkins agent (a Windows server with VMware PowerCLI installed) authenticates to whichever vCenter environments are selected, runs the disk operations, and writes a timestamped transcript log. On failure, an alert email is dispatched automatically.
✅ Key Benefits
Speed
A task that takes 15–20 minutes manually completes in under 2 minutes end-to-end.
Safety Checks
Datastore free-space validated before any change — aborts if post-expansion space would fall below 10%.
Audit Trail
Full timestamped transcript log and Jenkins build history provide ready-made change evidence.
Secure Credentials
Passwords never appear in the script — injected at runtime by Jenkins Credentials Binding.
Dual-Site Support
Prod and DR vCenter operations in a single job — select one or both via a checkbox.
Failure Alerts
Exception emails dispatched automatically so the team knows immediately if something goes wrong.
🔧 Prerequisites
VMware PowerCLI installed on the Jenkins Windows agent
Run Install-Module -Name VMware.PowerCLI -Scope AllUsers on the agent machine. Minimum version: 12.x.
VMware.VimAutomation.Core module available
Required for Get-VMGuestDisk and related cmdlets. Bundled with PowerCLI 12+.
Jenkins Credentials configured
Store the vCenter service account password as a Secret Text credential in Jenkins. The binding name maps to $Env:Password.
Log folder created on the agent
Create C:\AWR_vSphere_Automation\Disk_Modify_PS_Logs\ on the Windows agent, or update the path in the script to suit your environment.
SMTP relay accessible from the agent
The script sends alert emails via an internal SMTP relay. Ensure port 25 is open between the agent and your relay host.
📜 The PowerCLI Script
The script (Expand-VMDKDisk.ps1) is fully refactored using PowerShell functions to eliminate repetition. The core logic remains identical to the original — only structure and naming have been improved. Key functions:
- Connect-VCenter — connects to Prod or DR vCenter and handles connection failure with email alert.
- Get-DiskSizeGB — safely retrieves live disk capacity, returning
$nullif the disk doesn't exist. - Expand-HardDisk — validates the four pre-conditions and expands one disk, logging the outcome.
- Send-AlertEmail — centralised email dispatch used by all failure paths.
#
# Expand-VMDKDisk.ps1
# Automates VMDK disk expansion across Prod and DR vCenters via Jenkins.
# automatewithravi.com — v2.0
#
# ── Transcript Logging ────────────────────────────────────────────────────
Start-Transcript -Path "C:\AWR_vSphere_Automation\Disk_Modify_PS_Logs\Logfile_$((Get-Date).ToString('MM-dd-yyyy_hhmmss')).txt" -NoClobber -IncludeInvocationHeader
# ── Environment Variables ─────────────────────────────────────────────────
$Prod_Vcenter = $Env:Prod_Vcenter
$DR_Vcenter = $Env:DR_Vcenter
$Prod_VCenter_Name = "prod-vcenter.automatewithravi.com"
$DR_VCenter_Name = "dr-vcenter.automatewithravi.com"
$Username = $Env:Username
$Password = $Env:Password
# ── Helper: Send Alert Email ──────────────────────────────────────────────
function Send-AlertEmail {
param([string]$Subject, [string]$Body)
Send-MailMessage -SmtpServer "smtp.automatewithravi.com" `
-From "vmware-automation@automatewithravi.com" `
-To "it-admin@automatewithravi.com" `
-Subject $Subject -Body $Body
}
# ── Helper: Connect to vCenter ────────────────────────────────────────────
function Connect-VCenter {
param([string]$Server, [string]$Label)
$conn = Connect-VIServer -Server $Server -User $Username -Password $Password
if (-not $conn.IsConnected) {
Write-Host " Unable to connect to $Label vCenter." -ForegroundColor Red
Send-AlertEmail -Subject "vCenter Failure - $Label" -Body "Failed to connect to $Label ($Server)."
Exit 1
}
Write-Host " Connected to $Label vCenter." -ForegroundColor Green
return $conn
}
# ── Helper: Expand a Single Hard Disk ────────────────────────────────────
function Expand-HardDisk {
param($VMName, $VM, $DiskLabel, $InputFlag, $ExpectedCurrentGB, $TargetGB, $ActualCurrentGB)
if ($InputFlag -eq "yes" -and $ExpectedCurrentGB -eq $ActualCurrentGB -and $TargetGB -gt $ActualCurrentGB) {
Write-Host " Expanding $DiskLabel → $TargetGB GB" -ForegroundColor Cyan
$disk = Get-HardDisk -VM $VM -Name $DiskLabel
Set-HardDisk -HardDisk $disk -CapacityGB $TargetGB -Confirm:$false | Out-Null
Write-Host " $DiskLabel expanded." -ForegroundColor Green
} else {
Write-Host " $DiskLabel skipped. Current: $ActualCurrentGB GB" -ForegroundColor Gray
}
}
# ═══════════════════════════════════════════════════════════════════════════
# PHASE 1 — vCenter Connections
# ═══════════════════════════════════════════════════════════════════════════
$Prod_Connection = $null
$DR_Connection = $null
if ($Prod_Vcenter -eq "yes") { $Prod_Connection = Connect-VCenter -Server $Prod_VCenter_Name -Label "Prod" }
if ($DR_Vcenter -eq "yes") { $DR_Connection = Connect-VCenter -Server $DR_VCenter_Name -Label "DR" }
# ═══════════════════════════════════════════════════════════════════════════
# PHASE 2 — Disk Operations
# ═══════════════════════════════════════════════════════════════════════════
$VMName = $Env:VM_Name
$VM = Get-VM -Name $VMName
# ISO disconnect before datastore check
$cd = Get-CDDrive -VM $VMName
if ($cd.ConnectionState.Connected -or $cd.ConnectionState.StartConnected -or ($null -ne $cd.IsoPath)) {
Get-VM -Name $VMName | Get-CDDrive | Set-CDDrive -NoMedia -Confirm:$false | Out-Null
}
# Datastore validation
$totalRequired = $hddIncrease + $disksizeGB_newHDD
Get-Datastore -RelatedObject $VM | ForEach-Object {
$afterPct = (($_.FreeSpaceGB - $totalRequired) / $_.CapacityGB) * 100
if ($afterPct -le 10) { Write-Host "ABORT: insufficient datastore space." -ForegroundColor Red; Exit 1 }
}
# Expand disks 1–5 using the function
for ($i = 0; $i -lt 5; $i++) {
Expand-HardDisk -VMName $VMName -VM $VM -DiskLabel $diskNames[$i] `
-InputFlag $hddFlags[$i] -ExpectedCurrentGB $hddCurrent[$i] `
-TargetGB $hddTarget[$i] -ActualCurrentGB $liveSize[$i]
}
Stop-Transcript
⚙️ Jenkins Parameterised Build Setup
Step 1 — Create a New Freestyle Job
In Jenkins, click New Item, name it something like VMware-VMDK-Disk-Expansion, select Freestyle project, and click OK.
Step 2 — Enable "This project is parameterised"
Under the General tab, tick This project is parameterised. This exposes the parameter builder where you add all inputs shown in the reference table below.
Step 3 — Add a Choice Parameter for vCenter selection
Add a Choice Parameter named Prod_Vcenter with choices yes / no. Repeat for DR_Vcenter. Engineers pick which environment(s) to target at run time.
Step 4 — Bind the vCenter password as a Secret
Go to Build Environment, tick Use secret text(s) or file(s), and add a Secret text binding. Set the variable name to Password and select the stored Jenkins credential that holds the vCenter service account password.
Step 5 — Set the Build Step
Under Build, add a Windows PowerShell (or Execute Windows batch command) build step. Reference the script using:
# Windows PowerShell build step
& "C:\AWR_vSphere_Automation\Scripts\Expand-VMDKDisk.ps1"
Step 6 — Set the Executor Label
Under Restrict where this project can be run, enter the label of your Windows agent that has PowerCLI installed (e.g. windows-powercli-agent). This ensures the job never lands on a Linux agent that lacks the VMware modules.
Step 7 — Archive the Log as a Build Artifact
Under Post-build Actions, add Archive the artifacts and set the pattern to:
C:\AWR_vSphere_Automation\Disk_Modify_PS_Logs\*.txt. This makes the transcript log downloadable from every build page for change management evidence.
📋 Jenkins Parameters Reference
| Parameter Name | Type | Example Value | Description |
|---|---|---|---|
Prod_Vcenter | Choice | yes / no | Target Production vCenter? |
DR_Vcenter | Choice | yes / no | Target DR vCenter? |
Username | String | svc-vmware | vCenter service account username |
Password | Secret | — | Injected from Jenkins Credentials — never a plain string |
VM_Name | String | web-prod-01 | Exact VM name in vCenter |
HDD1 | Choice | yes / no | Expand Hard disk 1? |
HDD1_CurrentSize | String | 100 | Current size in GB (must match vCenter for safety) |
HDD1_Disk_Size_Total_Increase | String | 200 | Target total size in GB after expansion |
HDD1_Size_Increaserequired | String | 100 | Delta GB — used for datastore space calculation |
| ↳ Repeat HDD2–HDD5 parameters following the same pattern | |||
New_HDD_required | Choice | yes / no | Create a brand-new disk? |
disksizeGB_new_HDD | String | 50 | Size of new disk in GB (only if New_HDD_required = yes) |
▶️ Running the Build
Click "Build with Parameters"
From the job's main page, click Build with Parameters. Jenkins displays a form with all configured parameters.
Fill in the change request values
Enter the VM name, select which disks to expand, enter the current and target sizes per the approved change request.
Select the target environment(s)
Set Prod_Vcenter and/or DR_Vcenter to yes. You can target both simultaneously.
Click Build and monitor the console
The console output shows live progress: connection status, datastore checks, per-disk expansion results, and the final disk summary table.
Download the transcript log
From the build's Artifacts section, download the .txt transcript and attach it to your change request ticket as completion evidence.
🛡️ Built-in Safety Checks
The script enforces four conditions before touching any disk:
| Check | What it validates | On failure |
|---|---|---|
| Input flag | Engineer must explicitly set the disk to yes |
Disk skipped silently — no change made |
| Size match | Provided current size must equal live vCenter value | Disk skipped — prevents expanding the wrong disk |
| Target > current | Target size must exceed current size (VMware cannot shrink disks) | Disk skipped — avoids invalid API call |
| Datastore space | Post-expansion free space must remain above 10% | Entire job aborts with Exit 1 and alert email |
Set-HardDisk call and a potentially confusing error from the vCenter API.
📦 Source Code on GitHub
The complete script (with full error handling, summary output, and comments) is hosted in the VMware repository on GitHub. Once you have reviewed the script preview above, the file will be pushed to the repository below.
Automatewithravi / VMware
Expand-VMDKDisk.ps1 — VMDK disk expansion automation for Jenkins · v2.0