VMware Cloud Community
JLogan2016
Enthusiast
Enthusiast
Jump to solution

Expanding a disk inside guest as part of VM build

I have been reading a lot of articles online, and reading through the many times I have seen this question has been asked here on the forums, but haven't seen a concrete answer. I currently have a build script I supply to customers for building new servers, both Windows and Linux flavors. The customer populates a web form or thick client with all necessary info, and the script adds the machine to A.D., selects a free IP address from their tool of choice, builds the VM, and adds it to the requisite GPOs. The only piece I have not been able to work out is the correlation of in-guest drives with VM disks. Currently the web form has slots for number of disks and size, but the admin building the machine is still required to go in manually and initialize the disk and assign a drive letter.

I have seen a number of suggestions around using invoke-vmscript, but most of them break down to using diskpart. For example, if I have a drive letter assigned already, I can do something like this, to use the max size:

$VolumeLetter = "e"

    Invoke-VMScript -vm $VM `

                    -ScriptText "echo rescan > c:\diskpart.txt && echo select vol $VolumeLetter >> c:\diskpart.txt && echo extend >> c:\diskpart.txt && diskpart.exe /s c:\diskpart.txt" `

                    -ScriptType BAT

But this doesn't help me on raw disks. If I have to log in to set the drive letter, the work is already done. I have also come across a number of posts about using WMI through invoke-vmscript but folks seem to run into the same frustrations as I am; pulling back raw disk info is easy enough, but assigning the drive letter does not work.

So I am just curious if anyone has this successfully, and reliably, running in their environment.

0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

There have been a number of attempts, but in my experience, none of these is 100% fool-proof.
Since I understand that you are dealing with a freshly installed system, some of these methods might work for you.
But then you seem to mention that you handle Win and Lin guest OS installations, which none of the current scripts I know can handle.

In short, I don't think there is currently a one-size-fits-all solution for your issue.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

0 Kudos
8 Replies
LucD
Leadership
Leadership
Jump to solution

There have been a number of attempts, but in my experience, none of these is 100% fool-proof.
Since I understand that you are dealing with a freshly installed system, some of these methods might work for you.
But then you seem to mention that you handle Win and Lin guest OS installations, which none of the current scripts I know can handle.

In short, I don't think there is currently a one-size-fits-all solution for your issue.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
JLogan2016
Enthusiast
Enthusiast
Jump to solution

Thanks for the reply, I was afraid of such. At present I am only worried about drives on the Windows side (the script has branching logic based on OS), but to further complicate things I have found a number of customers lately that block PSRemoting, so as soon as the server reboots and picks up GPO that avenue is closed to me as well.

0 Kudos
LucD
Leadership
Leadership
Jump to solution

Running a script inside the guest OS through Invoke-VMScript is not a form of PS Remoting.

I don't think a PS Remoting GPO can block that.
Unless of course, they block the use of PS altogether.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
JLogan2016
Enthusiast
Enthusiast
Jump to solution

No, you are correct, I run other scripts through Invoke-VMScript with no issues. I just meant that upon hitting a snag getting this process to work with Invoke-VMScript I thought to simply do it all remotely with PS Remoting, but too often that is blocked. Each time I build a server I am making a non-persistent copy of the customization spec and then modifying; I am looking at how I might use the Run Once to potentially do what I want. It won't happen until someone logs in, but in testing I can initialize, apply a drive letter, and convert upwards of 10 drives to dynamic in less than a minute.

0 Kudos
JLogan2016
Enthusiast
Enthusiast
Jump to solution

Just to circle back, for anyone who may search for similar in the future, this is as far as I have come thus far:

The build script reads in the csv, producing an array of machines to be built, like so:

NameCPURAMIPNetMaskGatewayDNS1DNS2PortGroupFolderDescriptionCompute_ClusterDataStore_ClusterTemplateLocationDomainSQLDrive1SizeDrive2LetterDrive2SizeDrive2NameDrive3LetterDrive3SizeDrive3NameDrive4LetterDrive4SizeDrive4NameDrive5LetterDrive5SizeDrive5NameOSTypeOSArchOwnerEnvironmentRequest
JLoTest0124172.24.16.25255.255.248.0172.24.16.6172.24.16.2172.24.16.3dvportGroup_VLAN22Testing

Testing

CXO_ProductionCMO_I_Non-Prod2016_DEOKINTRANo100F30LogsG150AppsI90BackupJ120RolloverWindows2016HostingSandbox123456

The foreach loop takes each VM object and runs it through the various build processes. At the end, on the newly built server, if I pull in that same VM object from the csv, I can use this function to prepare the disks:

Function _initializeDisks {

   [CmdLetBinding()]

   Param (

      [Parameter(Mandatory, Position=1)]

         $oServer

    )

    $aFinal = @()

    $aRawDisks = gwmi win32_diskdrive |

                     where {$_.Partitions -eq 0} |

                        Select @{n="Number";e={$_.index}}, @{n="Size"; e={[math]::Round(((($_.Size / 1024) /1024) / 1024))}} |

                           Sort Number

    foreach($a in 2..5) {

        $sLetter = "Drive$($a)Letter"

        $sSize = "Drive$($a)Size"

        $sName = "Drive$($a)Name"

        if(!([string]::IsNullOrEmpty($oServer.$sSize))) {

            foreach ($disk in $aRawDisks) {

                if ($oServer.$sSize -eq $disk.Size) {

                    $aFinal += New-Object PSObject -Property @{DriveLetter=$($oServer.$sLetter);Number=$($disk.Number);Size=$($disk.Size);Description=$($oServer.$sName)}

                }      

            }

        }

    }

    foreach($newDrive in $aFinal) {

       Initialize-Disk -Number $newDrive.Number -PartitionStyle GPT

       $sDrive = New-Partition -DiskNumber $newDrive.Number -AssignDriveLetter -UseMaximumSize

       Format-Volume -DriveLetter $sDrive.DriveLetter -FileSystem NTFS

       Set-Volume -DriveLetter $sDrive.DriveLetter -NewFileSystemLabel $newDrive.Description

       Set-Partition -DriveLetter $sDrive.DriveLetter -NewDriveLetter $newDrive.DriveLetter

       Start-Sleep 1

       $sCommand = "select disk $($newDrive.Number)`r`nconvert dynamic"

       $sCommand | diskpart

    }

}

This works great, but again has to be after someone logs into the new server and manually runs it. I have yet to be able to use Invoke-VMScript to do this process; cannot seem to pass the VM object through. Hope to overcome that hurdle at some point and make this fully automated.

0 Kudos
LucD
Leadership
Leadership
Jump to solution

I'm still not sure that I understand what the problem is you have with Invoke-VMScript when assigning the harddisks and then creating a drive inside the guest OS.
When you add a harddisk (with New-HardDisk), you could then run inside the guest OS (through Invoke-VMScript) a script to discover this new disk, format it and assign a drive letter.
Repeat for the number of harddisks required.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos
JLogan2016
Enthusiast
Enthusiast
Jump to solution

The issue I am having is passing that $VM object in through the Invoke-VMScript. Something like this, for example:

$script = '&"C:\Users\Me\Desktop\InitializeDisks.ps1"'

Get-VM -Name "JLoTest" |

    Invoke-VMScript -ScriptText $script -GuestCredential $creds

Throws errors because of one of more missing mandatory parameters (oServer). However if I try to pass the $VM object to the script like so:

$script = '&"C:\Users\Me\Desktop\InitializeDisks.ps1 -oServer "' + $VM

Get-VM -Name "JLoTest" |

    Invoke-VMScript -ScriptText $script -GuestCredential $creds

It throws an error, like it cannot parse through the object.

ScriptOutput : JLoTest : The term 'JLoTest' is not recognized as the name of a cmdlet, function, script file,

               or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and

               try again.

               At line:1 char:69

              + ... sktop\InitializeDisks.ps1 -oServer "@{Name=JLoTest; NumCPU ...

               +                                                ~~~~~~~~~~~~~~~~~~

I have confirmed I can run scripts requiring no parameters, or simple strings, through Invoke-VMScript. Just not sure the correct syntax to pass the $VM object in,

0 Kudos
LucD
Leadership
Leadership
Jump to solution

You need to use variable substitution for that.

See my Here strings and the ExpandString method dive.

Your code could be something like this

$script = @'

&"C:\Users\Me\Desktop\InitializeDisks.ps1 -oServer $vmName"

'@


$vmName = 'JLoTest'

$scriptSub = $ExecutionContext.InvokeCommand.ExpandString($script)


Invoke-VMScript -VM $vmName -ScriptText $scriptSub -GuestCredential $creds


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

0 Kudos