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 } }