🔍 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.

💡 The script supports expanding up to five VMDK disks per run and can optionally create a brand-new hard disk — all from the Jenkins build console, with zero RDP or vCenter UI access required.

🏗️ 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

1

VMware PowerCLI installed on the Jenkins Windows agent

Run Install-Module -Name VMware.PowerCLI -Scope AllUsers on the agent machine. Minimum version: 12.x.

2

VMware.VimAutomation.Core module available

Required for Get-VMGuestDisk and related cmdlets. Bundled with PowerCLI 12+.

3

Jenkins Credentials configured

Store the vCenter service account password as a Secret Text credential in Jenkins. The binding name maps to $Env:Password.

4

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.

5

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:

PowerShell · Expand-VMDKDisk.ps1
#
# 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
The full script (with all imports, error handling, and summary output) is available on GitHub below. The snippet above shows the key structural patterns.

⚙️ 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.

🔐 Never type passwords as String Parameters — they appear in build logs in plain text. Always use the Credentials Binding Plugin to inject secrets as environment variables.

Step 5 — Set the Build Step

Under Build, add a Windows PowerShell (or Execute Windows batch command) build step. Reference the script using:

Jenkins Build Step
# 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_CurrentSizeString 100 Current size in GB (must match vCenter for safety)
HDD1_Disk_Size_Total_IncreaseString200 Target total size in GB after expansion
HDD1_Size_Increaserequired String100 Delta GB — used for datastore space calculation
↳ Repeat HDD2–HDD5 parameters following the same pattern
New_HDD_requiredChoice yes / no Create a brand-new disk?
disksizeGB_new_HDDString50 Size of new disk in GB (only if New_HDD_required = yes)

▶️ Running the Build

1

Click "Build with Parameters"

From the job's main page, click Build with Parameters. Jenkins displays a form with all configured parameters.

2

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.

3

Select the target environment(s)

Set Prod_Vcenter and/or DR_Vcenter to yes. You can target both simultaneously.

4

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.

5

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:

CheckWhat it validatesOn 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
⚠️ VMware does not allow shrinking a VMDK. The script's target-greater-than-current check prevents an invalid 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

View on GitHub →
VMware vSphere PowerCLI Jenkins CI/CD VMDK Disk Expansion Automation PowerShell Infrastructure Automation