So I’ve just started working with PowerShell 5 and DSC. I am attempting to create a resource that creates a Virtual Machine. However, currently the frustration is too much and I need some pointers here.
I’m not exactly sure if this issue is related to an underlying call inside of the PowerCLI 6.0 Module but it seems to me that I currently do not fully understand why DSC is being weird. I have attempted to load the Module required at the top of my psm1 file. However it seems when the call is made to Connect-VIServer that the module is no longer loaded.
As I recall in watching the Advanced DSC course on MVA, Jason or Jeffrey mentioned that each of the steps was spawned in its own PowerShell instance. So it makes sense that I would need to reload the Module inside of each call to Get/Set/Test. What is troubling is that when I do this, the first call to Connect-VIServer works great. (In the Test function)
Once DSC enters into SET mode and calls the Set function I receive the following error.
An item with the same key has already been added.
+ CategoryInfo : NotSpecified: (:) [], CimException
+ FullyQualifiedErrorId : System.ArgumentException,Microsoft.PowerShell.Commands.AddPSSnapinCommand
+ PSComputerName : localhost
In reading this I assumed that PowerShell/WMI had already loaded the module but was in the process of unloading it. So I tried to sleep for a few seconds before the Import-Module command. No Luck… What am I missing here?
Here is my code for review:
enum VMPowerState
{
poweredOff
poweredOn
suspended
}
enum Ensure
{
absent
present
}
[DscResource()]
class VirtualMachine
{
[DscProperty(Key)]
[String]$Name
[DscProperty(Mandatory)]
[Ensure]$Ensure
[DscProperty(Mandatory)]
[String]$Vcenter
[DscProperty(Mandatory)]
[PSCredential]$VcenterCredential
[DscProperty(Mandatory)]
[String]$Datacenter
[String]$ResourcePool
[DscProperty(Mandatory)]
[UInt32]$Vcpu
[DscProperty(Mandatory)]
[UInt32]$MemoryMb
[DscProperty(Mandatory)]
[VMPowerState]$PowerState
[String]$DefaultGateway
[String]$InitialFolder
[String]$InitialCluster
[DscProperty(Mandatory)]
[String]$InitialTemplate
[String]$InitialDatastore
[VirtualMachine]Get()
{
if ($this.Find())
{
Write-Verbose -Message "Found a Virtual Machine with Name: $($this.Name)"
} else {
Write-Verbose -Message "Could not find a Virtual Machine with Name: $($this.Name)"
}
return $this
}
[void] Set()
{
$this.Connect()
try {
if ($this.Exists()) {
$this.Update()
}
else {
$this.Create()
}
} finally {
$this.Disconnect()
}
}
[bool] Test()
{
return $this.Compare()
}
[void] LoadModules()
{
Import-Module -Name "VMware.VimAutomation.Core"
}
[bool] Exists()
{
try {
Get-VM -Name $this.Name
return $true
} catch {
return $false
}
}
[void] Create()
{
Write-Verbose -Message "Creating VM with Name: $($this.Name)"
}
[void] Update()
{
$this.Connect()
try {
$vm = Get-VM -Name $this.Name
} catch {
return
} finally {
$this.Disconnect()
}
if ($vm.NumCpu.ToString() -ne $this.Vcpu)
{
Write-Verbose -Message "Set-VM -NumCpu $($this.Vcpu)"
}
if ($vm.MemoryMB.ToString() -ne $this.MemoryMb)
{
Write-Verbose -Message "Set-VM -MemoryMB $($this.MemoryMb)"
}
if ($vm.PowerState.ToString() -ne $this.PowerState)
{
switch($this.PowerState)
{
[VMPowerState]::poweredOff
{
Write-Verbose -Message "Stop-VM -VM $vm"
}
[VMPowerState]::poweredOn
{
Write-Verbose -Message "Start-VM -VM $vm"
}
[VMPowerState]::suspended
{
Write-Verbose -Message "Suspend-VM -VM $vm"
}
}
}
}
[bool] Find()
{
$this.Connect()
try {
$vm = Get-VM -Name $this.Name
} catch {
return $false
} finally {
$this.Disconnect()
}
$this.Datacenter = $(Get-Datacenter -VM $vm).Name
$this.ResourcePool = $vm.ResourcePool.Name
$this.Vcpu = $vm.NumCpu.ToString()
$this.MemoryMb = $vm.MemoryMB.ToString()
$this.PowerState = $vm.PowerState.ToString()
#####
# The Guest state may be 'NotRunning' even though the VM is powered on
# If the VMWare Tools package is not installed or installed improperly
# This will cause network information about the VM to be unavailable
#####
$guestAvailable = $vm.Guest.State -eq "Running"
if ($guestAvailable)
{
$this.DefaultGateway = ($vm.Guest.ExtensionData.IpStack.IPRouteConfig.IpRoute |
Where-Object { $_.Network -eq "0.0.0.0" }).Gateway.IPAddress
}
return $true
}
[bool] Compare()
{
$this.Connect()
try {
$vm = Get-VM -Name $this.Name
} catch {
return ($this.Ensure -eq [Ensure]::absent)
} finally {
$this.Disconnect()
}
return `
(
($this.Ensure -eq [Ensure]::present) -and `
($vm.NumCpu.ToString() -eq $this.Vcpu) -and `
($vm.MemoryMB.ToString() -eq $this.MemoryMb) -and `
($vm.PowerState.ToString() -eq $this.PowerState)
)
}
[void] Connect()
{
$this.LoadModules()
Write-Verbose -Message "Connecting to VCenter at: $($this.Vcenter)"
Connect-VIServer -Server $this.Vcenter -Credential $this.VcenterCredential
}
[void] Disconnect()
{
Write-Verbose -Message "Disconnecting from VCenter at: $($this.Vcenter)"
Disconnect-VIServer -Server $this.Vcenter
}
}