VMware.vSphereDSC.psm1
<#
Copyright (c) 2018-2021 VMware, Inc. All rights reserved The BSD-2 license (the "License") set forth below applies to all parts of the Desired State Configuration Resources for VMware project. You may not use this file except in compliance with the License. BSD-2 License Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #> Using module '.\VMware.vSphereDSC.Helper.psm1' Using module '.\VMware.vSphereDSC.Logging.psm1' enum AcceptanceLevel { VMwareCertified VMwareAccepted PartnerSupported CommunitySupported } enum ChapType { Prohibited Discouraged Preferred Required Unset } enum DomainAction { Join Leave } enum DRSRuleType { VMAffinity VMAntiAffinity } enum Duplex { Full Half Unset } enum Ensure { Absent Present } enum EntityType { Folder Datacenter Cluster Datastore DatastoreCluster VMHost ResourcePool VApp VM Template } enum FolderType { Network Datastore Vm Host } enum GraphicsType { Shared SharedDirect } enum IScsiHbaTargetType { Static Send } enum LinkDiscoveryProtocolOperation { Advertise Both Listen None Unset } enum LinkDiscoveryProtocolProtocol { CDP LLDP Unset } enum LoadBalancingPolicy { LoadBalanceIP LoadBalanceSrcMac LoadBalanceSrcId ExplicitFailover Unset } enum LoggingLevel { Unset None Error Warning Info Verbose Trivia } enum MultipathPolicy { Fixed MostRecentlyUsed RoundRobin Unknown Unset } enum NetworkFailoverDetectionPolicy { LinkStatus BeaconProbing Unset } enum Period { Day = 86400 Week = 604800 Month = 2629800 Year = 31556926 } enum PortBinding { Static Dynamic Ephemeral Unset } enum PowerPolicy { HighPerformance = 1 Balanced = 2 LowPower = 3 Custom = 4 } enum ServicePolicy { Unset On Off Automatic } enum SharedPassthruAssignmentPolicy { Performance Consolidation } enum VMHostState { Connected Disconnected Maintenance Unset } enum VMSwapfilePolicy { InHostDatastore WithVM Unset } enum VsanDataMigrationMode { Full EnsureAccessibility NoDataMigration Unset } enum AccessMode { ReadWrite ReadOnly } enum AuthenticationMethod { AUTH_SYS Kerberos } enum NicTeamingPolicy { Loadbalance_ip Loadbalance_srcmac Loadbalance_srcid Failover_explicit Unset } enum DrsAutomationLevel { FullyAutomated Manual PartiallyAutomated Disabled Unset } enum HAIsolationResponse { PowerOff DoNothing Shutdown Unset } enum HARestartPriority { Disabled Low Medium High Unset } enum BadCertificateAction { Ignore Warn Prompt Fail Unset } enum DefaultVIServerMode { Single Multiple Unset } enum PowerCLISettingsScope { LCM } enum ProxyPolicy { NoProxy UseSystemProxy Unset } class BasevSphereConnection { <# .DESCRIPTION Credentials needed for connection to the specified Server. #> [DscProperty()] [PSCredential] $Credential <# .DESCRIPTION Established connection to the specified vSphere Server. #> [PSObject] $Connection <# .DESCRIPTION Saves logs in a HashTable ref in order to retrieve the stream output from outside of Invoke-DscResource execution. #> [ref] $Logs hidden [string] $DscResourceName = $this.GetType().Name hidden [string] $DscResourceIsInDesiredStateMessage = "{0} DSC Resource is in Desired State." hidden [string] $DscResourceIsNotInDesiredStateMessage = "{0} DSC Resource is not in Desired State." hidden [string] $TestMethodStartMessage = "Begin executing Test functionality for {0} DSC Resource." hidden [string] $TestMethodEndMessage = "End executing Test functionality for {0} DSC Resource." hidden [string] $SetMethodStartMessage = "Begin executing Set functionality for {0} DSC Resource." hidden [string] $SetMethodEndMessage = "End executing Set functionality for {0} DSC Resource." hidden [string] $GetMethodStartMessage = "Begin executing Get functionality for {0} DSC Resource." hidden [string] $GetMethodEndMessage = "End executing Get functionality for {0} DSC Resource." hidden [string] $SettingIsNotInDesiredStateMessage = "Setting {0}: Current setting value [ {1} ] does not match desired setting value [ {2} ]." hidden [string] $DscResourcesEnumDefaultValue = 'Unset' hidden [string] $VCenterConnectionRequiredMessage = "The DSC Resource operations are only supported when connection is directly to a vCenter." hidden [string] $ESXiConnectionRequiredMessage = "The DSC Resource operations are only supported when connection is directly to an ESXi host." hidden [string] $CouldNotEstablishvSphereConnectionMessage = "Could not establish connection to vSphere Server {0}. For more information: {1}" hidden [string] $CouldNotClosevSphereConnectionMessage = "Could not close connection to vSphere Server {0}. For more information: {1}" hidden [string] $vCenterProductId = 'vpx' hidden [string] $ESXiProductId = 'embeddedEsx' <# .DESCRIPTION Imports the required modules where the used PowerCLI cmdlets reside. #> [void] ImportRequiredModules() { <# The Verbose logic here is needed to suppress the Verbose output of the Import-Module cmdlet when importing the 'VMware.VimAutomation.Core' Module. #> $savedVerbosePreference = $global:VerbosePreference $global:VerbosePreference = 'SilentlyContinue' Import-Module -Name 'VMware.VimAutomation.Core' $global:VerbosePreference = $savedVerbosePreference } <# .DESCRIPTION Checks if the passed array is in the desired state and if an update should be performed. #> [bool] ShouldUpdateArraySetting($settingName, $currentArray, $desiredArray) { $result = $null if ($null -eq $desiredArray) { # The property is not specified. $result = $false } elseif ($desiredArray.Length -eq 0 -and $currentArray.Length -ne 0) { # Empty array specified as desired, but current is not an empty array, so update should be performed. $this.WriteLogUtil('Verbose', $this.SettingIsNotInDesiredStateMessage, @($settingName, ($currentArray -Join ', '), ($desiredArray -Join ', '))) $result = $true } else { $elementsToAdd = $desiredArray | Where-Object { $currentArray -NotContains $_ } $elementsToRemove = $currentArray | Where-Object { $desiredArray -NotContains $_ } if ($null -ne $elementsToAdd -or $null -ne $elementsToRemove) { <# The current array does not contain at least one element from desired array or the desired array is a subset of the current array. In both cases we should perform an update operation. #> $this.WriteLogUtil('Verbose', $this.SettingIsNotInDesiredStateMessage, @($settingName, ($currentArray -Join ', '), ($desiredArray -Join ', '))) $result = $true } else { # No need to perform an update operation. $result = $false } } return $result } <# .DESCRIPTION Checks if the passed setting is in the desired state and if an update should be performed. #> [bool] ShouldUpdateDscResourceSetting($settingName, $currentSetting, $desiredSetting) { $result = $null if ($this.$settingName -is [string]) { $result = ($null -ne $desiredSetting -and $desiredSetting -ne [string] $currentSetting) } elseif ($this.$settingName -is [enum]) { $result = ($desiredSetting -ne $this.DscResourcesEnumDefaultValue -and $desiredSetting -ne $currentSetting) } else { $result = ($null -ne $desiredSetting -and $desiredSetting -ne $currentSetting) } if ($result) { $this.WriteLogUtil('Verbose', $this.SettingIsNotInDesiredStateMessage, @($settingName, $currentSetting, $desiredSetting)) } return $result } <# .DESCRIPTION Checks if the Connection is directly to a vCenter and if not, throws an exception. #> [void] EnsureConnectionIsvCenter() { if ($this.Connection.ProductLine -ne $this.vCenterProductId) { throw $this.VCenterConnectionRequiredMessage } } <# .DESCRIPTION Checks if the Connection is directly to an ESXi host and if not, throws an exception. #> [void] EnsureConnectionIsESXi() { if ($this.Connection.ProductLine -ne $this.ESXiProductId) { throw $this.ESXiConnectionRequiredMessage } } <# .DESCRIPTION Writes a Verbose message specifying if the DSC Resource is in the Desired State. #> [void] WriteDscResourceState($result) { $messageToUse = [string]::Empty if ($result) { $messageToUse = $this.DscResourceIsInDesiredStateMessage } else { $messageToUse = $this.DscResourceIsNotInDesiredStateMessage } $this.WriteLogUtil('Verbose', $messageToUse, @($this.DscResourceName)) } <# .DESCRIPTION Checks if the specified VIObject is of the specified type. #> [bool] IsVIObjectOfTheCorrectType($viObject, $typeAsString) { $result = $false $viObjectType = $viObject.GetType() if ($viObjectType.FullName -eq $typeAsString) { $result = $true } elseif (($viObjectType.GetInterfaces().FullName -eq $typeAsString).Length -gt 0) { $result = $true } else { $baseType = $viObjectType.BaseType while ($null -ne $baseType) { if ($baseType.FullName -eq $typeAsString) { $result = $true break } $baseType = $baseType.BaseType } } return $result } <# .DESCRIPTION Connects to the specified vSphere Server with the passed Credentials. The method sets the Connection property to the established connection. If connection cannot be established, the method throws an exception. #> [void] ConnectVIServer() { $this.ImportRequiredModules() if ($null -eq $this.Connection) { try { $connectVIServerParams = @{ Server = $this.Server Credential = $this.Credential ErrorAction = 'Stop' Verbose = $false } $this.Connection = Connect-VIServer @connectVIServerParams } catch { throw ($this.CouldNotEstablishvSphereConnectionMessage -f $this.Server, $_.Exception.Message) } } else { # this is a connection from a vSphereDSC node # a new connection with the same session and server name get created because of an issue with runspaces in PowerShell $this.Connection = Connect-VIServer -Session $this.Connection.SessionSecret -Server $this.Connection.Name } } <# .DESCRIPTION Closes the last open connection to the specified vSphere Server. #> [void] DisconnectVIServer() { if ($null -eq $this.Connection) { return } try { $disconnectVIServerParams = @{ Server = $this.Connection Confirm = $false ErrorAction = 'Stop' Verbose = $false } Disconnect-VIServer @disconnectVIServerParams } catch { throw ($this.CouldNotClosevSphereConnectionMessage -f $this.Connection.Name, $_.Exception.Message) } } <# .DESCRIPTION Logs a message to the correct information stream and to a property. #> [void] WriteLogUtil($logType, $message, $arguments) { $writeLogSplat = @{ Message = $message } # this variable will be added to the logs hashtable $logMessage = $message if ($null -ne $arguments) { $writeLogSplat['Arguments'] = $arguments $logMessage = [string]::Format($Message, $Arguments) } # write to warning or verbose stream if ($logType -eq 'Verbose') { Write-VerboseLog @writeLogSplat } elseif ($logType -eq 'Warning') { Write-WarningLog @writeLogSplat } if ($null -eq $this.Logs) { return } $this.Logs.Value.Add([PsObject]@{ Type = $logType Message = $logMessage }) | Out-Null } [void] WriteLogUtil($logType, $message) { $this.WriteLogUtil($logType, $message, $null) } } class BaseDSC : BasevSphereConnection { <# .DESCRIPTION Name of the Server we are trying to connect to. The Server can be a vCenter or ESXi. #> [DscProperty()] [string] $Server } class DatacenterInventoryBaseDSC : BaseDSC { <# .DESCRIPTION Name of the Inventory Item located in the Datacenter specified in 'DatacenterName' key property. #> [DscProperty(Key)] [string] $Name <# .DESCRIPTION Location of the Inventory Item with name specified in 'Name' key property in the Datacenter specified in the 'DatacenterName' key property. Location consists of 0 or more Inventory Items. Empty Location means that the Inventory Item is in the Root Folder of the Datacenter ('Vm', 'Host', 'Network' or 'Datastore' based on the Inventory Item). The Root Folders of the Datacenter are not part of the Location. Inventory Item names in Location are separated by "/". Example Location for a VM Inventory Item: "Discovered Virtual Machines/My Ubuntu VMs". #> [DscProperty(Key)] [string] $Location <# .DESCRIPTION Name of the Datacenter we will use from the specified Inventory. #> [DscProperty(Key)] [string] $DatacenterName <# .DESCRIPTION Location of the Datacenter we will use from the Inventory. Root Folder of the Inventory is not part of the Location. Empty Location means that the Datacenter is in the Root Folder of the Inventory. Folder names in Location are separated by "/". Example Location: "MyDatacentersFolder". #> [DscProperty(Key)] [string] $DatacenterLocation <# .DESCRIPTION Value indicating if the Inventory Item should be Present or Absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION Type of Folder in which the Inventory Item is located. Possible values are VM, Network, Datastore, Host. #> hidden [FolderType] $InventoryItemFolderType <# .DESCRIPTION Ensures the correct behaviour when the Location is not valid based on the passed Ensure value. If Ensure is set to 'Present' and the Location is not valid, the method should throw with the passed error message. Otherwise Ensure is set to 'Absent' and $null result is returned because with invalid Location, the Inventory Item is 'Absent' from that Location and no error should be thrown. #> [PSObject] EnsureCorrectBehaviourForInvalidLocation($expression) { if ($this.Ensure -eq [Ensure]::Present) { throw $expression } return $null } <# .DESCRIPTION Returns the Datacenter we will use from the Inventory. #> [PSObject] GetDatacenter() { $rootFolderAsViewObject = Get-View -Server $this.Connection -Id $this.Connection.ExtensionData.Content.RootFolder $rootFolder = Get-Inventory -Server $this.Connection -Id $rootFolderAsViewObject.MoRef # Special case where the Location does not contain any folders. if ($this.DatacenterLocation -eq [string]::Empty) { $foundDatacenter = Get-Datacenter -Server $this.Connection -Name $this.DatacenterName -Location $rootFolder -ErrorAction SilentlyContinue | Where-Object { $_.ParentFolderId -eq $rootFolder.Id } if ($null -eq $foundDatacenter) { return $this.EnsureCorrectBehaviourForInvalidLocation("Datacenter $($this.DatacenterName) was not found at $($rootFolder.Name).") } return $foundDatacenter } # Special case where the Location is just one folder. if ($this.DatacenterLocation -NotMatch '/') { $foundLocation = Get-Folder -Server $this.Connection -Name $this.DatacenterLocation -Location $rootFolder -ErrorAction SilentlyContinue | Where-Object { $_.ParentId -eq $rootFolder.Id } if ($null -eq $foundLocation) { return $this.EnsureCorrectBehaviourForInvalidLocation("Folder $($this.DatacenterLocation) was not found at $($rootFolder.Name).") } $foundDatacenter = Get-Datacenter -Server $this.Connection -Name $this.DatacenterName -Location $foundLocation -ErrorAction SilentlyContinue | Where-Object { $_.ParentFolderId -eq $foundLocation.Id } if ($null -eq $foundDatacenter) { return $this.EnsureCorrectBehaviourForInvalidLocation("Datacenter $($this.DatacenterName) was not found at $($foundLocation.Name).") } return $foundDatacenter } $locationItems = $this.DatacenterLocation -Split '/' $childEntities = Get-View -Server $this.Connection -Id $rootFolder.ExtensionData.ChildEntity $foundLocationItem = $null for ($i = 0; $i -lt $locationItems.Length; $i++) { $locationItem = $locationItems[$i] $foundLocationItem = $childEntities | Where-Object -Property Name -eq $locationItem if ($null -eq $foundLocationItem) { return $this.EnsureCorrectBehaviourForInvalidLocation("Datacenter $($this.DatacenterName) with Location $($this.DatacenterLocation) was not found because $locationItem folder cannot be found below $($rootFolder.Name).") } # If the found location item does not have 'ChildEntity' member, the item is a Datacenter. $childEntityMember = $foundLocationItem | Get-Member -Name 'ChildEntity' if ($null -eq $childEntityMember) { return $this.EnsureCorrectBehaviourForInvalidLocation("The Location $($this.DatacenterLocation) contains another Datacenter $locationItem.") } <# If the found location item is a Folder we check how many Child Entities the folder has: If the Folder has zero Child Entities and the Folder is not the last location item, the Location is not valid. Otherwise we start looking in the items of this Folder. #> if ($foundLocationItem.ChildEntity.Length -eq 0) { if ($i -ne $locationItems.Length - 1) { return $this.EnsureCorrectBehaviourForInvalidLocation("The Location $($this.DatacenterLocation) is not valid because Folder $locationItem does not have Child Entities and the Location $($this.DatacenterLocation) contains other Inventory Items.") } } else { $childEntities = Get-View -Server $this.Connection -Id $foundLocationItem.ChildEntity } } $foundLocation = Get-Inventory -Server $this.Connection -Id $foundLocationItem.MoRef $foundDatacenter = Get-Datacenter -Server $this.Connection -Name $this.DatacenterName -Location $foundLocation -ErrorAction SilentlyContinue | Where-Object { $_.ParentFolderId -eq $foundLocation.Id } if ($null -eq $foundDatacenter) { return $this.EnsureCorrectBehaviourForInvalidLocation("Datacenter $($this.DatacenterName) with Location $($this.DatacenterLocation) was not found.") } return $foundDatacenter } <# .DESCRIPTION Returns the Location of the Inventory Item from the specified Datacenter. #> [PSObject] GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) { <# Here if the Ensure property is set to 'Absent', we do not need to check if the Location is valid because the Datacenter does not exist and this means that the Inventory Item does not exist in the specified Datacenter. #> if ($null -eq $datacenter -and $this.Ensure -eq [Ensure]::Absent) { return $null } $validInventoryItemLocation = $null $datacenterFolderAsViewObject = Get-View -Server $this.Connection -Id $datacenter.ExtensionData.$datacenterFolderName $datacenterFolder = Get-Inventory -Server $this.Connection -Id $datacenterFolderAsViewObject.MoRef # Special case where the Location does not contain any Inventory Items. if ($this.Location -eq [string]::Empty) { return $datacenterFolder } # Special case where the Location is just one Inventory Item. if ($this.Location -NotMatch '/') { $validInventoryItemLocation = Get-Inventory -Server $this.Connection -Name $this.Location -Location $datacenterFolder -ErrorAction SilentlyContinue | Where-Object { $_.ParentId -eq $datacenterFolder.Id } if ($null -eq $validInventoryItemLocation) { return $this.EnsureCorrectBehaviourForInvalidLocation("Location $($this.Location) of Inventory Item $($this.Name) was not found in Folder $($datacenterFolder.Name).") } return $validInventoryItemLocation } $locationItems = $this.Location -Split '/' # Reverses the location items so that we can start from the bottom and go to the top of the Inventory. [array]::Reverse($locationItems) $datacenterInventoryItemLocationName = $locationItems[0] $foundLocations = Get-Inventory -Server $this.Connection -Name $datacenterInventoryItemLocationName -Location $datacenterFolder -ErrorAction SilentlyContinue # Removes the Name of the Inventory Item Location from the location items array as we already retrieved it. $locationItems = $locationItems[1..($locationItems.Length - 1)] <# For every found Inventory Item Location in the Datacenter with the specified name we start to go up through the parents to check if the Location is valid. If one of the Parents does not meet the criteria of the Location, we continue with the next found Location. If we find a valid Location we stop iterating through the Locations and return it. #> foreach ($foundLocation in $foundLocations) { $foundLocationAsViewObject = Get-View -Server $this.Connection -Id $foundLocation.Id -Property Parent $validLocation = $true foreach ($locationItem in $locationItems) { $foundLocationAsViewObject = Get-View -Server $this.Connection -Id $foundLocationAsViewObject.Parent -Property Name, Parent if ($foundLocationAsViewObject.Name -ne $locationItem) { $validLocation = $false break } } if ($validLocation) { $validInventoryItemLocation = $foundLocation break } } if ($null -eq $validInventoryItemLocation) { return $this.EnsureCorrectBehaviourForInvalidLocation("Location $($this.Location) of Inventory Item $($this.Name) was not found in Datacenter $($datacenter.Name).") } return $validInventoryItemLocation } <# .DESCRIPTION Returns the Inventory Item from the specified Location in the Datacenter if it exists, otherwise returns $null. #> [PSObject] GetInventoryItem($inventoryItemLocationInDatacenter) { return Get-Inventory -Server $this.Connection -Name $this.Name -Location $inventoryItemLocationInDatacenter -ErrorAction SilentlyContinue | Where-Object { $_.ParentId -eq $inventoryItemLocationInDatacenter.Id } } } class VMHostEntityBaseDSC : BaseDSC { <# .DESCRIPTION Name of the VMHost which is going to be used. #> [DscProperty(Key)] [string] $VMHostName <# .DESCRIPTION The VMHost which is going to be used. #> hidden [PSObject] $VMHost hidden [string] $RetrieveVMHostMessage = "Retrieving VMHost {0} from vCenter {1}." hidden [string] $CouldNotRetrieveVMHostMessage = "Could not retrieve VMHost {0} from vCenter {1}. For more information: {2}" <# .DESCRIPTION Retrieves the VMHost with the specified name from the server. If the VMHost is not found, it throws an exception. #> [void] RetrieveVMHost() { try { $this.WriteLogUtil('Verbose', $this.RetrieveVMHostMessage, @($this.VMHostName, $this.Connection.Name)) $getVMHostParams = @{ Server = $this.Connection Name = $this.VMHostName ErrorAction = 'Stop' Verbose = $false } $this.VMHost = Get-VMHost @getVMHostParams } catch { throw ($this.CouldNotRetrieveVMHostMessage -f $this.VMHostName, $this.Connection.Name, $_.Exception.Message) } } } class DatastoreBaseDSC : VMHostEntityBaseDSC { <# .DESCRIPTION Specifies the name of the Datastore. #> [DscProperty(Key)] [string] $Name <# .DESCRIPTION For Nfs Datastore, specifies the remote path of the Nfs mount point. For Vmfs Datastore, specifies the canonical name of the Scsi logical unit that contains the Vmfs Datastore. #> [DscProperty(Mandatory)] [string] $Path <# .DESCRIPTION Specifies whether the Datastore should be present or absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION Specifies the file system that is used on the Datastore. #> [DscProperty()] [string] $FileSystemVersion <# .DESCRIPTION Specifies the latency period beyond which the storage array is considered congested. The range of this value is between 10 to 100 milliseconds. #> [DscProperty()] [nullable[int]] $CongestionThresholdMillisecond <# .DESCRIPTION Indicates whether the IO control is enabled. #> [DscProperty()] [nullable[bool]] $StorageIOControlEnabled hidden [string] $CreateDatastoreMessage = "Creating Datastore {0} on VMHost {1}." hidden [string] $ModifyDatastoreMessage = "Modifying Datastore {0} on VMHost {1}." hidden [string] $RemoveDatastoreMessage = "Removing Datastore {0} from VMHost {1}." hidden [string] $CouldNotCreateDatastoreMessage = "Could not create Datastore {0} on VMHost {1}. For more information: {2}" hidden [string] $CouldNotModifyDatastoreMessage = "Could not modify Datastore {0} on VMHost {1}. For more information: {2}" hidden [string] $CouldNotRemoveDatastoreMessage = "Could not remove Datastore {0} from VMHost {1}. For more information: {2}" <# .DESCRIPTION Retrieves the Datastore with the specified name from the VMHost if it exists. #> [PSObject] GetDatastore() { $getDatastoreParams = @{ Server = $this.Connection Name = $this.Name VMHost = $this.VMHost ErrorAction = 'SilentlyContinue' Verbose = $false } return Get-Datastore @getDatastoreParams } <# .DESCRIPTION Checks if the specified Datastore should be modified. #> [bool] ShouldModifyDatastore($datastore) { $shouldModifyDatastore = @( $this.ShouldUpdateDscResourceSetting('CongestionThresholdMillisecond', $datastore.CongestionThresholdMillisecond, $this.CongestionThresholdMillisecond), $this.ShouldUpdateDscResourceSetting('StorageIOControlEnabled', $datastore.StorageIOControlEnabled, $this.StorageIOControlEnabled) ) return ($shouldModifyDatastore -Contains $true) } <# .DESCRIPTION Creates a new Datastore with the specified name on the VMHost. #> [PSObject] NewDatastore($newDatastoreParams) { $newDatastoreParams.Server = $this.Connection $newDatastoreParams.Name = $this.Name $newDatastoreParams.VMHost = $this.VMHost $newDatastoreParams.Path = $this.Path $newDatastoreParams.Confirm = $false $newDatastoreParams.ErrorAction = 'Stop' $newDatastoreParams.Verbose = $false if (![string]::IsNullOrEmpty($this.FileSystemVersion)) { $newDatastoreParams.FileSystemVersion = $this.FileSystemVersion } try { $this.WriteLogUtil('Verbose', $this.CreateDatastoreMessage, @($this.Name, $this.VMHost.Name)) $datastore = New-Datastore @newDatastoreParams return $datastore } catch { throw ($this.CouldNotCreateDatastoreMessage -f $this.Name, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Modifies the properties of the specified Datastore. #> [void] ModifyDatastore($datastore) { $setDatastoreParams = @{ Server = $this.Connection Datastore = $datastore Confirm = $false ErrorAction = 'Stop' Verbose = $false } if ($null -ne $this.StorageIOControlEnabled) { $setDatastoreParams.StorageIOControlEnabled = $this.StorageIOControlEnabled } if ($null -ne $this.CongestionThresholdMillisecond) { $setDatastoreParams.CongestionThresholdMillisecond = $this.CongestionThresholdMillisecond } try { $this.WriteLogUtil('Verbose', $this.ModifyDatastoreMessage, @($datastore.Name, $this.VMHost.Name)) Set-Datastore @setDatastoreParams } catch { throw ($this.CouldNotModifyDatastoreMessage -f $datastore.Name, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Removes the specified Datastore from the VMHost. #> [void] RemoveDatastore($datastore) { $removeDatastoreParams = @{ Server = $this.Connection Datastore = $datastore VMHost = $thiS.VMHost Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.RemoveDatastoreMessage, @($datastore.Name, $this.VMHost.Name)) Remove-Datastore @removeDatastoreParams } catch { throw ($this.CouldNotRemoveDatastoreMessage -f $datastore.Name, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $datastore) { $result.Server = $this.Connection.Name $result.VMHostName = $this.VMHost.Name if ($null -ne $datastore) { $result.Name = $datastore.Name $result.Ensure = [Ensure]::Present $result.FileSystemVersion = $datastore.FileSystemVersion $result.CongestionThresholdMillisecond = $datastore.CongestionThresholdMillisecond $result.StorageIOControlEnabled = $datastore.StorageIOControlEnabled } else { $result.Name = $this.Name $result.Ensure = [Ensure]::Absent $result.FileSystemVersion = $this.FileSystemVersion $result.CongestionThresholdMillisecond = $this.CongestionThresholdMillisecond $result.StorageIOControlEnabled = $this.StorageIOControlEnabled } } } class VMHostBaseDSC : BaseDSC { <# .DESCRIPTION Specifies the name of the VMHost to configure. #> [DscProperty(Key)] [string] $Name hidden [string] $CouldNotRetrieveVMHostMessage = "Could not retrieve VMHost {0} on Server {1}. For more information: {2}" <# .DESCRIPTION Retrieves the VMHost with the specified name from the specified Server. If the VMHost is not found, the method throws an exception. #> [PSObject] GetVMHost() { try { $getVMHostParams = @{ Server = $this.Connection Name = $this.Name ErrorAction = 'Stop' Verbose = $false } return Get-VMHost @getVMHostParams } catch { throw ($this.CouldNotRetrieveVMHostMessage -f $this.Name, $this.Connection.Name, $_.Exception.Message) } } } class EsxCliBaseDSC : VMHostBaseDSC { <# .DESCRIPTION The PowerCLI EsxCli version 2 interface to ESXCLI. #> hidden [PSObject] $EsxCli <# .DESCRIPTION The EsxCli command for the DSC Resource that inherits the base class. For the DCUI Keyboard DSC Resource the command is the following: 'system.settings.keyboard.layout'. #> hidden [string] $EsxCliCommand <# .DESCRIPTION The name of the DSC Resource that inherits the base class. #> hidden [string] $DscResourceName = $this.GetType().Name hidden [string] $EsxCliAddMethodName = 'add' hidden [string] $EsxCliSetMethodName = 'set' hidden [string] $EsxCliRemoveMethodName = 'remove' hidden [string] $EsxCliGetMethodName = 'get' hidden [string] $EsxCliListMethodName = 'list' hidden [string] $CouldNotRetrieveEsxCliInterfaceMessage = "Could not retrieve EsxCli interface for VMHost {0}. For more information: {1}" hidden [string] $CouldNotCreateMethodArgumentsMessage = "Could not create arguments for {0} method. For more information: {1}" hidden [string] $EsxCliCommandFailedMessage = "EsxCli command {0} failed to execute successfully. For more information: {1}" <# .DESCRIPTION Retrieves the EsxCli version 2 interface to ESXCLI for the specified VMHost. #> [void] GetEsxCli($vmHost) { try { $this.EsxCli = Get-EsxCli -Server $this.Connection -VMHost $vmHost -V2 -ErrorAction Stop -Verbose:$false } catch { throw ($this.CouldNotRetrieveEsxCliInterfaceMessage -f $vmHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Executes the specified method for modification - 'set', 'add' or 'remove' of the specified EsxCli command. #> [void] ExecuteEsxCliModifyMethod($methodName, $methodArguments) { $esxCliCommandMethod = "$($this.EsxCliCommand).$methodName." $esxCliMethodArgs = $null try { $esxCliMethodArgs = Invoke-Expression -Command ("`$this.EsxCli." + $esxCliCommandMethod + 'CreateArgs()') -ErrorAction Stop -Verbose:$false } catch { throw ($this.CouldNotCreateMethodArgumentsMessage -f $methodName, $_.Exception.Message) } # Skips the properties that are defined in the base classes of the Dsc Resource because they are not arguments of the EsxCli command. $dscResourceNamesOfProperties = $this.GetType().GetProperties() | Where-Object -FilterScript { $_.DeclaringType.Name -eq $this.DscResourceName } | Select-Object -ExpandProperty Name # A separate array of keys is needed because collections cannot be modified while being enumerated. $commandArgs = @() $commandArgs = $commandArgs + $esxCliMethodArgs.Keys foreach ($key in $commandArgs) { # The argument of the method is present in the method arguments hashtable and should be used instead of the property of the Dsc Resource. if ($methodArguments.Count -gt 0 -and $null -ne $methodArguments.$key) { $esxCliMethodArgs.$key = $methodArguments.$key } else { # The name of the property of the Dsc Resource starts with a capital letter whereas the key of the argument contains only lower case letters. $dscResourcePropertyName = $dscResourceNamesOfProperties | Where-Object -FilterScript { $_.ToLower() -eq $key.ToLower() } # Not all properties of the Dsc Resource are part of the arguments hashtable. if ($null -ne $dscResourcePropertyName) { if ($this.$dscResourcePropertyName -is [string]) { if (![string]::IsNullOrEmpty($this.$dscResourcePropertyName)) { $esxCliMethodArgs.$key = $this.$dscResourcePropertyName } } elseif ($this.$dscResourcePropertyName -is [array]) { if ($null -ne $this.$dscResourcePropertyName -and $this.$dscResourcePropertyName.Length -gt 0) { $esxCliMethodArgs.$key = $this.$dscResourcePropertyName } } else { if ($null -ne $this.$dscResourcePropertyName) { $esxCliMethodArgs.$key = $this.$dscResourcePropertyName } } } } } try { Invoke-EsxCliCommandMethod -EsxCli $this.EsxCli -EsxCliCommandMethod ($esxCliCommandMethod + 'Invoke({0})') -EsxCliCommandMethodArguments $esxCliMethodArgs } catch { throw ($this.EsxCliCommandFailedMessage -f ('esxcli.' + $this.EsxCliCommand), $_.Exception.Message) } } <# .DESCRIPTION Executes the specified method for modification - 'set', 'add' or 'remove' of the specified EsxCli command. #> [void] ExecuteEsxCliModifyMethod($methodName) { $this.ExecuteEsxCliModifyMethod($methodName, @{}) } <# .DESCRIPTION Executes the specified retrieval method - 'get' or 'list' of the specified EsxCli command. #> [PSObject] ExecuteEsxCliRetrievalMethod($methodName) { $esxCliCommandMethod = '$this.EsxCli.' + "$($this.EsxCliCommand).$methodName." try { $esxCliCommandMethodResult = Invoke-Expression -Command ($esxCliCommandMethod + 'Invoke()') -ErrorAction Stop -Verbose:$false return $esxCliCommandMethodResult } catch { throw ($this.EsxCliCommandFailedMessage -f ('esxcli.' + $this.EsxCliCommand), $_.Exception.Message) } } } class InventoryBaseDSC : BaseDSC { <# .DESCRIPTION Name of the Inventory Item (Folder or Datacenter) located in the Folder specified in 'Location' key property. #> [DscProperty(Key)] [string] $Name <# .DESCRIPTION Location of the Inventory Item (Folder or Datacenter) we will use from the Inventory. Root Folder of the Inventory is not part of the Location. Empty Location means that the Inventory Item (Folder or Datacenter) is in the Root Folder of the Inventory. Folder names in Location are separated by "/". Example Location: "MyDatacenters". #> [DscProperty(Key)] [string] $Location <# .DESCRIPTION Value indicating if the Inventory Item (Folder or Datacenter) should be Present or Absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION Ensures the correct behaviour when the Location is not valid based on the passed Ensure value. If Ensure is set to 'Present' and the Location is not valid, the method should throw with the passed error message. Otherwise Ensure is set to 'Absent' and $null result is returned because with invalid Location, the Inventory Item is 'Absent' from that Location and no error should be thrown. #> [PSObject] EnsureCorrectBehaviourForInvalidLocation($expression) { if ($this.Ensure -eq [Ensure]::Present) { throw $expression } return $null } <# .DESCRIPTION Returns the Location of the Inventory Item (Folder or Datacenter) from the specified Inventory. #> [PSObject] GetInventoryItemLocation() { $rootFolderAsViewObject = Get-View -Server $this.Connection -Id $this.Connection.ExtensionData.Content.RootFolder $rootFolder = Get-Inventory -Server $this.Connection -Id $rootFolderAsViewObject.MoRef # Special case where the Location does not contain any folders. if ($this.Location -eq [string]::Empty) { return $rootFolder } # Special case where the Location is just one folder. if ($this.Location -NotMatch '/') { $foundLocation = Get-Inventory -Server $this.Connection -Name $this.Location -Location $rootFolder -ErrorAction SilentlyContinue | Where-Object { $_.ParentId -eq $rootFolder.Id } if ($null -eq $foundLocation) { return $this.EnsureCorrectBehaviourForInvalidLocation("Folder $($this.Location) was not found at $($rootFolder.Name).") } return $foundLocation } $locationItems = $this.Location -Split '/' $childEntities = Get-View -Server $this.Connection -Id $rootFolder.ExtensionData.ChildEntity $foundLocationItem = $null for ($i = 0; $i -lt $locationItems.Length; $i++) { $locationItem = $locationItems[$i] $foundLocationItem = $childEntities | Where-Object -Property Name -eq $locationItem if ($null -eq $foundLocationItem) { return $this.EnsureCorrectBehaviourForInvalidLocation("Inventory Item $($this.Name) with Location $($this.Location) was not found because $locationItem folder cannot be found below $($rootFolder.Name).") } # If the found location item does not have 'ChildEntity' member, the item is a Datacenter. $childEntityMember = $foundLocationItem | Get-Member -Name 'ChildEntity' if ($null -eq $childEntityMember) { return $this.EnsureCorrectBehaviourForInvalidLocation("The Location $($this.Location) contains Datacenter $locationItem which is not valid.") } <# If the found location item is a Folder we check how many Child Entities the folder has: If the Folder has zero Child Entities and the Folder is not the last location item, the Location is not valid. Otherwise we start looking in the items of this Folder. #> if ($foundLocationItem.ChildEntity.Length -eq 0) { if ($i -ne $locationItems.Length - 1) { return $this.EnsureCorrectBehaviourForInvalidLocation("The Location $($this.Location) is not valid because Folder $locationItem does not have Child Entities and the Location $($this.Location) contains other Inventory Items.") } } else { $childEntities = Get-View -Server $this.Connection -Id $foundLocationItem.ChildEntity } } return Get-Inventory -Server $this.Connection -Id $foundLocationItem.MoRef } } class InventoryUtil { InventoryUtil($viServer, $ensure) { $this.VIServer = $viServer $this.Ensure = $ensure } <# .DESCRIPTION Specifies the established connection to the vCenter Server system. #> [PSObject] $VIServer <# .DESCRIPTION Specifies whether the Inventory Item that is exposed through a DSC Resource that uses the 'InventoryUtil' class should be present or absent. It is used to determine the behaviour of the methods in the class that retrieve Inventory Items. #> [Ensure] $Ensure hidden [string] $InvalidLocationMessage = "Location {0} is not a valid location inside Folder {1}." hidden [string] $CouldNotRetrieveRootFolderMessage = "Could not retrieve Inventory Root Folder of vCenter Server {0}. For more information: {1}" hidden [string] $CouldNotRetrieveDatacenterRootFolderMessage = "Could not retrieve {0} of Datacenter {1}. For more information: {2}" hidden [string] $CouldNotRetrieveVDSwitchMessage = "Could not retrieve VDSwitch {0}. For more information: {1}" hidden [string] $CouldNotFindDatacenterMessage = "Could not find Datacenter {0} located in Folder {1}." hidden [string] $CouldNotFindFolderMessage = "Could not find Folder {0} located in Folder {1}." hidden [string] $CouldNotFindInventoryItemMessage = "Could not find Inventory Item {0} located in Inventory Item {1}." hidden [string] $CouldNotFindDatastoreClusterMessage = "Could not find Datastore Cluster {0} located in Folder {1}." <# .DESCRIPTION Retrieves the Datacenter with the specified name, located in the specified Folder. #> [PSObject] GetDatacenter($datacenterName, $datacenterLocation) { $datacenter = $null $inventoryRootFolder = $this.GetInventoryRootFolder() <# If empty Datacenter location is passed, the Datacenter should be located in the Root Folder of the Inventory. If Datacenter location without '/' is passed, the Datacenter should be located in the Folder specified in the Datacenter location. If Datacenter location with '/' is passed, the Datacenter should be located in the Folder that is lastly specified in the Datacenter location. #> if ($datacenterLocation -eq [string]::Empty) { $datacenter = $this.GetDatacenterInFolder($datacenterName, $inventoryRootFolder) } elseif ($datacenterLocation -NotMatch '/') { $folder = $this.GetFolder($datacenterLocation, $inventoryRootFolder) $datacenter = $this.GetDatacenterInFolder($datacenterName, $folder) } else { $folder = $null $datacenterLocationItems = $datacenterLocation -Split '/' # The array needs to be reversed so we can retrieve the Folder where the Datacenter is located. [array]::Reverse($datacenterLocationItems) $datacenterLocationName = $datacenterLocationItems[0] $foundDatacenterFolderLocations = $this.GetDatacenterFoldersByName($datacenterLocationName, $inventoryRootFolder) # The Folder where the Datacenter is located is already retrieved and it's not needed anymore. $datacenterLocationItems = $datacenterLocationItems[1..($datacenterLocationItems.Length - 1)] foreach ($foundDatacenterFolderLocation in $foundDatacenterFolderLocations) { $currentDatacenterLocationItem = $foundDatacenterFolderLocation $isDatacenterLocationValid = $true foreach ($datacenterLocationItem in $datacenterLocationItems) { if ($currentDatacenterLocationItem.Parent.Name -ne $datacenterLocationItem) { $isDatacenterLocationValid = $false break } $currentDatacenterLocationItem = $currentDatacenterLocationItem.Parent } <# If the Datacenter location is valid, the first Datacenter location item should be located inside the Root Folder of the Inventory. #> if ( $isDatacenterLocationValid -and $currentDatacenterLocationItem.ParentId -eq $inventoryRootFolder.Id ) { $folder = $foundDatacenterFolderLocation break } } if ($null -eq $folder -and $this.Ensure -eq [Ensure]::Present) { throw ($this.InvalidLocationMessage -f $datacenterLocation, $inventoryRootFolder.Name) } $datacenter = $this.GetDatacenterInFolder($datacenterName, $folder) } return $datacenter } <# .DESCRIPTION Retrieves the Parent of the Inventory Item located in the specified Datacenter. #> [PSObject] GetInventoryItemParent($inventoryItemLocation, $datacenter, $datacenterRootFolderName) { # If the Datacenter doesn't exist, the Inventory Item Parent doesn't exist as well. if ($null -eq $datacenter) { return $null } $inventoryItemParent = $null $datacenterRootFolder = $this.GetRootFolderOfDatacenter($datacenter, $datacenterRootFolderName) <# If empty Inventory Item location is passed, the Inventory Item Parent in the Root Folder of the Datacenter. If Inventory Item location without '/' is passed, the Inventory Item Parent is the Inventory Item specified in the Inventory Item location. If Inventory Item location with '/' is passed, the Inventory Item Parent is the Inventory Item that is lastly specified in the Inventory Item location. #> if ($inventoryItemLocation -eq [string]::Empty) { $inventoryItemParent = $datacenterRootFolder } elseif ($inventoryItemLocation -NotMatch '/') { $inventoryItemParent = $this.GetInventoryItem($inventoryItemLocation, $datacenterRootFolder) } else { $inventoryItemLocationItems = $inventoryItemLocation -Split '/' # The array needs to be reversed so we can retrieve the name of the Parent of the Inventory Item. [array]::Reverse($inventoryItemLocationItems) $inventoryItemParentName = $inventoryItemLocationItems[0] $foundInventoryItemLocations = $this.GetInventoryItemsByName($inventoryItemParentName, $datacenterRootFolder) # The Parent name where the Inventory Item is located is already retrieved and it's not needed anymore. $inventoryItemLocationItems = $inventoryItemLocationItems[1..($inventoryItemLocationItems.Length - 1)] foreach ($foundInventoryItemLocation in $foundInventoryItemLocations) { $currentInventoryItemLocationItem = $foundInventoryItemLocation $isInventoryItemLocationValid = $true foreach ($inventoryItemLocationItem in $inventoryItemLocationItems) { if ($currentInventoryItemLocationItem.Parent.Name -ne $inventoryItemLocationItem) { $isInventoryItemLocationValid = $false break } $currentInventoryItemLocationItem = $currentInventoryItemLocationItem.Parent } <# If the Inventory Item location is valid, the first Inventory Item location item should be located inside the corresponding Root Folder of the Datacenter. #> if ( $isInventoryItemLocationValid -and $currentInventoryItemLocationItem.ParentId -eq $datacenterRootFolder.Id ) { $inventoryItemParent = $foundInventoryItemLocation break } } if ($null -eq $inventoryItemParent -and $this.Ensure -eq [Ensure]::Present) { throw ($this.InvalidLocationMessage -f $inventoryItemLocation, $datacenterRootFolder.Name) } } return $inventoryItemParent } <# .DESCRIPTION Retrieves the Root Folder of the specified Inventory. #> [PSObject] GetInventoryRootFolder() { $getInventoryParams = @{ Server = $this.VIServer Id = $this.VIServer.ExtensionData.Content.RootFolder ErrorAction = 'Stop' Verbose = $false } try { return Get-Inventory @getInventoryParams } catch { throw ($this.CouldNotRetrieveRootFolderMessage -f $this.VIServer.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the specified Root Folder of the Datacenter. #> [PSObject] GetRootFolderOfDatacenter($datacenter, $datacenterRootFolderName) { $getInventoryParams = @{ Server = $this.VIServer Id = $datacenter.ExtensionData.$datacenterRootFolderName ErrorAction = 'Stop' Verbose = $false } try { return Get-Inventory @getInventoryParams } catch { throw ($this.CouldNotRetrieveDatacenterRootFolderMessage -f $datacenterRootFolderName, $datacenter.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the Datacenter with the specified name, located in the specified Folder. #> [PSObject] GetDatacenterInFolder($datacenterName, $folder) { $getDatacenterParams = @{ Server = $this.VIServer Name = $datacenterName Location = $folder ErrorAction = 'SilentlyContinue' Verbose = $false } $whereObjectParams = @{ FilterScript = { $_.ParentFolderId -eq $folder.Id } } $datacenter = Get-Datacenter @getDatacenterParams | Where-Object @whereObjectParams if ($null -eq $datacenter -and $this.Ensure -eq [Ensure]::Present) { throw ($this.CouldNotFindDatacenterMessage -f $datacenterName, $folder.Name) } return $datacenter } <# .DESCRIPTION Retrieves the Folder with the specified name, located in the specified Folder. #> [PSObject] GetFolder($folderName, $parentFolder) { $getFolderParams = @{ Server = $this.VIServer Name = $folderName Location = $parentFolder ErrorAction = 'SilentlyContinue' Verbose = $false } $whereObjectParams = @{ FilterScript = { $_.ParentId -eq $parentFolder.Id } } $folder = Get-Folder @getFolderParams | Where-Object @whereObjectParams if ($null -eq $folder -and $this.Ensure -eq [Ensure]::Present) { throw ($this.CouldNotFindFolderMessage -f $folderName, $parentFolder.Name) } return $folder } <# .DESCRIPTION Retrieves all Folders with the specified name of type Datacenter, located inside the specified Folder. #> [PSObject] GetDatacenterFoldersByName($folderName, $folderLocation) { $getFolderParams = @{ Server = $this.VIServer Name = $folderName Location = $folderLocation Type = 'Datacenter' ErrorAction = 'SilentlyContinue' Verbose = $false } return Get-Folder @getFolderParams } <# .DESCRIPTION Retrieves all Inventory Items with the specified name, located inside the specified Inventory Item. #> [array] GetInventoryItemsByName($inventoryItemName, $inventoryItemLocation) { $getInventoryParams = @{ Server = $this.VIServer Name = $inventoryItemName Location = $inventoryItemLocation ErrorAction = 'SilentlyContinue' Verbose = $false } return Get-Inventory @getInventoryParams } <# .DESCRIPTION Retrieves the Inventory Item with the specified name, located in the specified Inventory Item. #> [PSObject] GetInventoryItem($inventoryItemName, $inventoryItemParent) { $getInventoryParams = @{ Server = $this.VIServer Name = $inventoryItemName Location = $inventoryItemParent ErrorAction = 'SilentlyContinue' Verbose = $false } $whereObjectParams = @{ FilterScript = { $_.ParentId -eq $inventoryItemParent.Id } } $inventoryItem = Get-Inventory @getInventoryParams | Where-Object @whereObjectParams if ($null -eq $inventoryItem -and $this.Ensure -eq [Ensure]::Present) { throw ($this.CouldNotFindInventoryItemMessage -f $inventoryItemName, $inventoryItemParent.Name) } return $inventoryItem } <# .DESCRIPTION Retrieves the Datastore Cluster with the specified name located in the specified Folder. #> [PSObject] GetDatastoreCluster($datastoreClusterName, $folder) { $getDatastoreClusterParams = @{ Server = $this.VIServer Name = $datastoreClusterName Location = $folder ErrorAction = 'SilentlyContinue' Verbose = $false } $whereObjectParams = @{ FilterScript = { $_.ExtensionData.Parent -eq $folder.ExtensionData.MoRef } } <# Multiple Datastore Clusters with the same name can be present in a Datacenter. So we need to filter by the direct Parent Folder of the Datastore Cluster to retrieve the desired one. #> $datastoreCluster = Get-DatastoreCluster @getDatastoreClusterParams | Where-Object @whereObjectParams if ($null -eq $datastoreCluster -and $this.Ensure -eq [Ensure]::Present) { throw ($this.CouldNotFindDatastoreClusterMessage -f $datastoreClusterName, $folder.Name) } return $datastoreCluster } <# .DESCRIPTION Retrieves the VDSwitch with the specified name from the server if it exists. If the VDSwitch does not exist and Ensure is set to 'Absent', $null is returned. Otherwise the method throws an exception. #> [PSObject] GetVDSwitch($vdSwitchName) { <# The Verbose logic here is needed to suppress the Verbose output of the Import-Module cmdlet when importing the 'VMware.VimAutomation.Vds' Module. #> $savedVerbosePreference = $global:VerbosePreference $global:VerbosePreference = 'SilentlyContinue' $getVDSwitchParams = @{ Server = $this.VIServer Name = $vdSwitchName Verbose = $false } if ($this.Ensure -eq [Ensure]::Absent) { $getVDSwitchParams.ErrorAction = 'SilentlyContinue' } else { $getVDSwitchParams.ErrorAction = 'Stop' } try { return Get-VDSwitch @getVDSwitchParams } catch { throw ($this.CouldNotRetrieveVDSwitchMessage -f $vdSwitchName, $_.Exception.Message) } finally { $global:VerbosePreference = $savedVerbosePreference } } } class VMHostRestartBaseDSC : VMHostBaseDSC { <# .DESCRIPTION Specifies the time in minutes to wait for the VMHost to restart before timing out and aborting the operation. The default value is 5 minutes. #> [DscProperty()] [int] $RestartTimeoutMinutes = 5 hidden [string] $NotRespondingState = 'NotResponding' hidden [string] $MaintenanceState = 'Maintenance' hidden [string] $VMHostIsRestartedSuccessfullyMessage = "VMHost {0} is successfully restarted and in {1} State." hidden [string] $VMHostIsStillNotInDesiredStateMessage = "VMHost {0} is still not in {1} State." hidden [string] $RestartVMHostMessage = "Restarting VMHost {0}." hidden [string] $VMHostIsNotInMaintenanceModeMessage = "The Resource update operation requires the VMHost {0} to be in a Maintenance mode." hidden [string] $CouldNotRestartVMHostInTimeMessage = "VMHost {0} could not be restarted successfully in {1} minutes." hidden [string] $CouldNotRestartVMHostMessage = "Could not restart VMHost {0}. For more information: {1}" <# .DESCRIPTION Checks if the specified VMHost is in Maintenance mode and if not, throws an exception. #> [void] EnsureVMHostIsInMaintenanceMode($vmHost) { if ($vmHost.ConnectionState.ToString() -ne $this.MaintenanceState) { throw ($this.VMHostIsNotInMaintenanceModeMessage -f $vmHost.Name) } } <# .DESCRIPTION Ensures that the specified VMHost is restarted successfully in the specified period of time. If the elapsed time is longer than the desired time for restart, the method throws an exception. #> [void] EnsureRestartTimeoutIsNotReached($elapsedTimeInSeconds) { $timeSpan = New-TimeSpan -Seconds $elapsedTimeInSeconds if ($this.RestartTimeoutMinutes -le $timeSpan.Minutes) { throw ($this.CouldNotRestartVMHostInTimeMessage -f $this.Name, $this.RestartTimeoutMinutes) } } <# .DESCRIPTION Ensures that the specified VMHost is in a Desired State after successful restart operation. #> [void] EnsureVMHostIsInDesiredState($requiresVIServerConnection, $desiredState) { $sleepTimeInSeconds = 10 $elapsedTimeInSeconds = 0 while ($true) { $this.EnsureRestartTimeoutIsNotReached($elapsedTimeInSeconds) Start-Sleep -Seconds $sleepTimeInSeconds $elapsedTimeInSeconds += $sleepTimeInSeconds try { if ($requiresVIServerConnection) { $this.ConnectVIServer() } $vmHost = $this.GetVMHost() if ($vmHost.ConnectionState.ToString() -eq $desiredState) { break } $this.WriteLogUtil('Verbose', $this.VMHostIsStillNotInDesiredStateMessage, @($this.Name, $desiredState)) } catch { <# Here the message used in the try block is written again in the case when an exception is thrown when retrieving the VMHost or establishing a Connection. This way the user still gets notified that the VMHost is not in the Desired State. #> $this.WriteLogUtil('Verbose', $this.VMHostIsStillNotInDesiredStateMessage, @($this.Name, $desiredState)) } } $this.WriteLogUtil('Verbose', $this.VMHostIsRestartedSuccessfullyMessage, @($this.Name, $desiredState)) } <# .DESCRIPTION Restarts the specified VMHost so that the update of the VMHost Configuration is successful. #> [void] RestartVMHost($vmHost) { try { $this.WriteLogUtil('Verbose', $this.RestartVMHostMessage, @($vmHost.Name)) $restartVMHostParams = @{ Server = $this.Connection VMHost = $vmHost Confirm = $false ErrorAction = 'Stop' Verbose = $false } Restart-VMHost @restartVMHostParams } catch { throw ($this.CouldNotRestartVMHostMessage -f $vmHost.Name, $_.Exception.Message) } <# If the Connection is directly to a vCenter we do not need to establish a new connection so we pass $false to the method 'EnsureVMHostIsInCorrectState'. When the Connection is directly to an ESXi, after a successful restart the ESXi is down so new Connection needs to be established to check the ESXi state. So we pass $true to the method 'EnsureVMHostIsInCorrectState'. We also need to set the variable holding the current Connection to $null, so a new Connection can be established via the ConnectVIServer(). #> if ($this.Connection.ProductLine -eq $this.vCenterProductId) { $this.EnsureVMHostIsInDesiredState($false, $this.NotRespondingState) $this.EnsureVMHostIsInDesiredState($false, $this.MaintenanceState) } else { $this.Connection = $null $this.EnsureVMHostIsInDesiredState($true, $this.MaintenanceState) } } } class VMHostGraphicsBaseDSC : VMHostRestartBaseDSC { hidden [string] $CouldNotRetrieveGraphicsManagerMessage = "Could not retrieve the Graphics Manager of VMHost {0}. For more information: {1}" <# .DESCRIPTION Retrieves the Graphics Manager of the specified VMHost from the server. #> [PSObject] GetVMHostGraphicsManager($vmHost) { try { $vmHostGraphicsManager = Get-View -Server $this.Connection -Id $vmHost.ExtensionData.ConfigManager.GraphicsManager -ErrorAction Stop return $vmHostGraphicsManager } catch { throw ($this.CouldNotRetrieveGraphicsManagerMessage -f $vmHost.Name, $_.Exception.Message) } } <# .DESCRIPTION The enum value passed in the Configuration should be converted to string value by the following criteria: Shared => shared; SharedDevice => sharedDevice #> [string] ConvertEnumValueToServerValue($enumValue) { return $enumValue.ToString().Substring(0, 1).ToLower() + $enumValue.ToString().Substring(1) } } class VMHostIScsiHbaBaseDSC : VMHostEntityBaseDSC { <# .DESCRIPTION Specifies the type of the CHAP (Challenge Handshake Authentication Protocol). #> [DscProperty()] [ChapType] $ChapType = [ChapType]::Unset <# .DESCRIPTION Specifies the CHAP authentication name. #> [DscProperty()] [string] $ChapName <# .DESCRIPTION Specifies the CHAP authentication password. #> [DscProperty()] [string] $ChapPassword <# .DESCRIPTION Indicates that Mutual CHAP is enabled. #> [DscProperty()] [nullable[bool]] $MutualChapEnabled <# .DESCRIPTION Specifies the Mutual CHAP authentication name. #> [DscProperty()] [string] $MutualChapName <# .DESCRIPTION Specifies the Mutual CHAP authentication password. #> [DscProperty()] [string] $MutualChapPassword <# .DESCRIPTION Specifies whether to change the password for CHAP, Mutual CHAP or both. When the property is not specified or its value is $false, it is ignored. If the property is $true the passwords for CHAP and Mutual CHAP are changed to their desired values. #> [DscProperty()] [nullable[bool]] $Force hidden [string] $IScsiDeviceType = 'iSCSI' hidden [string] $CouldNotRetrieveIScsiHbaMessage = "Could not retrieve iSCSI Host Bus Adapter {0} from VMHost {1}. For more information: {2}" <# .DESCRIPTION Retrieves the iSCSI Host Bus Adapter with the specified name from the specified VMHost if it exists. #> [PSObject] GetIScsiHba($iScsiHbaName) { try { $iScsiHba = Get-VMHostHba -Server $this.Connection -VMHost $this.VMHost -Device $iScsiHbaName -Type $this.IScsiDeviceType -ErrorAction Stop -Verbose:$false return $iScsiHba } catch { throw ($this.CouldNotRetrieveIScsiHbaMessage -f $iScsiHbaName, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Checks if the CHAP settings should be modified based on the current authentication properties. #> [bool] ShouldModifyCHAPSettings($authenticationProperties, $inheritChap, $inheritMutualChap) { $shouldModifyCHAPSettings = @( $this.ShouldUpdateDscResourceSetting('InheritChap', $authenticationProperties.ChapInherited, $inheritChap), $this.ShouldUpdateDscResourceSetting('ChapType', [string] $authenticationProperties.ChapType, $this.ChapType.ToString()), $this.ShouldUpdateDscResourceSetting('InheritMutualChap', $authenticationProperties.MutualChapInherited, $inheritMutualChap), $this.ShouldUpdateDscResourceSetting('MutualChapEnabled', $authenticationProperties.MutualChapEnabled, $this.MutualChapEnabled), $this.ShouldUpdateDscResourceSetting('Force', $false, $this.Force) ) # CHAP and Mutual CHAP names should be ignored when determining the Desired State when CHAP type is 'Prohibited'. if ($this.ChapType -ne [ChapType]::Prohibited) { $shouldModifyCHAPSettings += $this.ShouldUpdateDscResourceSetting( 'ChapName', [string] $authenticationProperties.ChapName, $this.ChapName ) $shouldModifyCHAPSettings += $this.ShouldUpdateDscResourceSetting( 'MutualChapName', [string] $authenticationProperties.MutualChapName, $this.MutualChapName ) } return ($shouldModifyCHAPSettings -Contains $true) } <# .DESCRIPTION Checks if the CHAP settings should be modified based on the current authentication properties. #> [bool] ShouldModifyCHAPSettings($authenticationProperties) { return $this.ShouldModifyCHAPSettings($authenticationProperties, $null, $null) } <# .DESCRIPTION Populates the cmdlet parameters with the CHAP settings based on the following criteria: 1. CHAP settings can only be passed to the cmdlet if the 'InheritChap' option is not passed or it is passed with a '$false' value. 2. Mutual CHAP settings can only be passed to the cmdlet if the 'InheritMutualChap' option is not passed or it is passed with a '$false' value. 3. CHAP name and CHAP password can be passed to the cmdlet if the CHAP type is not 'Prohibited'. 4. Mutual CHAP settings can only be passed to the cmdlet if the CHAP type is 'Required'. 5. Mutual CHAP name and Mutual CHAP password can be passed to the cmdlet if Mutual CHAP enabled is not passed or if it is passed with a '$true' value. #> [void] PopulateCmdletParametersWithCHAPSettings($cmdletParams, $inheritChap, $inheritMutualChap) { if ($null -ne $inheritChap -and $inheritChap) { $cmdletParams.InheritChap = $inheritChap } else { # When 'InheritChap' is $false, it can be passed to the cmdlet only if CHAP type is not 'Prohibited'. if ($null -ne $inheritChap -and $this.ChapType -ne [ChapType]::Prohibited) { $cmdletParams.InheritChap = $inheritChap } if ($this.ChapType -ne [ChapType]::Unset) { $cmdletParams.ChapType = $this.ChapType.ToString() } if ($this.ChapType -ne [ChapType]::Prohibited) { if (![string]::IsNullOrEmpty($this.ChapName)) { $cmdletParams.ChapName = $this.ChapName } if (![string]::IsNullOrEmpty($this.ChapPassword)) { $cmdletParams.ChapPassword = $this.ChapPassword } } } if ($null -ne $inheritMutualChap -and $inheritMutualChap) { $cmdletParams.InheritMutualChap = $inheritMutualChap } else { # When 'InheritMutualChap' is $false, it can be passed to the cmdlet only if CHAP type is not 'Prohibited'. if ($null -ne $inheritMutualChap -and $this.ChapType -ne [ChapType]::Prohibited) { $cmdletParams.InheritMutualChap = $inheritMutualChap } if ($this.ChapType -eq [ChapType]::Required) { if ($null -ne $this.MutualChapEnabled) { $cmdletParams.MutualChapEnabled = $this.MutualChapEnabled } if ($null -eq $this.MutualChapEnabled -or $this.MutualChapEnabled) { if (![string]::IsNullOrEmpty($this.MutualChapName)) { $cmdletParams.MutualChapName = $this.MutualChapName } if (![string]::IsNullOrEmpty($this.MutualChapPassword)) { $cmdletParams.MutualChapPassword = $this.MutualChapPassword } } } } } <# .DESCRIPTION Populates the cmdlet parameters with the CHAP settings. #> [void] PopulateCmdletParametersWithCHAPSettings($cmdletParams) { $this.PopulateCmdletParametersWithCHAPSettings($cmdletParams, $null, $null) } } class VMHostNetworkBaseDSC : VMHostBaseDSC { hidden [PSObject] $VMHostNetworkSystem hidden [string] $CouldNotRetrieveNetworkSystemMessage = "Could not retrieve the Network System of VMHost {0}. For more information: {1}" <# .DESCRIPTION Retrieves the Network System from the specified VMHost. #> [void] GetNetworkSystem($vmHost) { try { $this.VMHostNetworkSystem = Get-View -Server $this.Connection -Id $vmHost.ExtensionData.ConfigManager.NetworkSystem -ErrorAction Stop } catch { throw ($this.CouldNotRetrieveNetworkSystemMessage -f $this.Name, $_.Exception.Message) } } } class VMHostNetworkMigrationBaseDSC : VMHostEntityBaseDSC { <# .DESCRIPTION Specifies the names of the Physical Network Adapters that should be part of the vSphere Distributed/Standard Switch. #> [DscProperty(Mandatory)] [string[]] $PhysicalNicNames <# .DESCRIPTION Specifies the names of the VMKernel Network Adapters that should be part of the vSphere Distributed/Standard Switch. #> [DscProperty()] [string[]] $VMKernelNicNames hidden [string] $RetrievePhysicalNicMessage = "Retrieving Physical Network Adapter {0} from VMHost {1}." hidden [string] $RetrieveVMKernelNicMessage = "Retrieving VMKernel Network Adapter {0} from VMHost {1}." hidden [string] $CouldNotFindPhysicalNicMessage = "Physical Network Adapter {0} was not found on VMHost {1} and will be ignored." hidden [string] $CouldNotFindVMKernelNicMessage = "VMKernel Network Adapter {0} was not found on VMHost {1}." <# .DESCRIPTION Retrieves the Physical Network Adapters with the specified names from the server if they exist. For every Physical Network Adapter that does not exist, a warning message is shown to the user without throwing an exception. #> [array] GetPhysicalNetworkAdapters() { $physicalNetworkAdapters = @() foreach ($physicalNetworkAdapterName in $this.PhysicalNicNames) { $this.WriteLogUtil('Verbose', $this.RetrievePhysicalNicMessage, @($physicalNetworkAdapterName, $this.VMHost.Name)) $getVMHostNetworkAdapterParams = @{ Server = $this.Connection Name = $physicalNetworkAdapterName VMHost = $this.VMHost Physical = $true ErrorAction = 'SilentlyContinue' Verbose = $false } $physicalNetworkAdapter = Get-VMHostNetworkAdapter @getVMHostNetworkAdapterParams if ($null -eq $physicalNetworkAdapter) { $this.WriteLogUtil('Warning', $this.CouldNotFindPhysicalNicMessage, @($physicalNetworkAdapterName, $this.VMHost.Name)) } else { $physicalNetworkAdapters += $physicalNetworkAdapter } } return $physicalNetworkAdapters } <# .DESCRIPTION Retrieves the VMKernel Network Adapters with the specified names from the server if they exist. If one of the passed VMKernel Network Adapters does not exist, an exception is thrown. #> [array] GetVMKernelNetworkAdapters() { $vmKernelNetworkAdapters = @() foreach ($vmKernelNetworkAdapterName in $this.VMKernelNicNames) { $this.WriteLogUtil('Verbose', $this.RetrieveVMKernelNicMessage, @($vmKernelNetworkAdapterName, $this.VMHost.Name)) $getVMHostNetworkAdapterParams = @{ Server = $this.Connection Name = $vmKernelNetworkAdapterName VMHost = $this.VMHost VMKernel = $true ErrorAction = 'Stop' Verbose = $false } try { $vmKernelNetworkAdapter = Get-VMHostNetworkAdapter @getVMHostNetworkAdapterParams $vmKernelNetworkAdapters += $vmKernelNetworkAdapter } catch { <# Here 'throw' should be used instead of 'Write-WarningLog' because if we ignore one VMKernel Network Adapter that is invalid, the mapping between VMKernel Network Adapters and Port Groups will not work: The first Adapter should be attached to the first Port Group, the second Adapter should be attached to the second Port Group, and so on. #> throw ($this.CouldNotFindVMKernelNicMessage -f $vmKernelNetworkAdapterName, $this.VMHost.Name) } } return $vmKernelNetworkAdapters } } class VMHostNicBaseDSC : VMHostEntityBaseDSC { <# .DESCRIPTION Specifies the name of the Port Group to which the VMKernel Network Adapter should be connected. If a Distributed Switch is passed, an existing Port Group name should be specified. For Standard Virtual Switches, if the Port Group is non-existent, a new Port Group with the specified name will be created and the VMKernel Network Adapter will be connected to it. #> [DscProperty(Key)] [string] $PortGroupName <# .DESCRIPTION Value indicating if the VMKernel Network Adapter should be Present or Absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION Indicates whether the VMKernel Network Adapter uses a Dhcp server. #> [DscProperty()] [nullable[bool]] $Dhcp <# .DESCRIPTION Specifies an IP address for the VMKernel Network Adapter. All IP addresses are specified using IPv4 dot notation. If IP is not specified, DHCP mode is enabled. #> [DscProperty()] [string] $IP <# .DESCRIPTION Specifies a Subnet Mask for the VMKernel Network Adapter. #> [DscProperty()] [string] $SubnetMask <# .DESCRIPTION Specifies a media access control (MAC) address for the VMKernel Network Adapter. #> [DscProperty()] [string] $Mac <# .DESCRIPTION Indicates that the IPv6 address is obtained through a router advertisement. #> [DscProperty()] [nullable[bool]] $AutomaticIPv6 <# .DESCRIPTION Specifies multiple static addresses using the following format: <IPv6>/<subnet_prefix_length> or <IPv6>. If you skip <subnet_prefix_length>, the default value of 64 is used. #> [DscProperty()] [string[]] $IPv6 <# .DESCRIPTION Indicates that the IPv6 address is obtained through DHCP. #> [DscProperty()] [nullable[bool]] $IPv6ThroughDhcp <# .DESCRIPTION Specifies the MTU size. #> [DscProperty()] [nullable[int]] $Mtu <# .DESCRIPTION Indicates that IPv6 configuration is enabled. Setting this parameter to $false disables all IPv6-related parameters. If the value is $true, you need to provide values for at least one of the IPv6 parameters. #> [DscProperty()] [nullable[bool]] $IPv6Enabled <# .DESCRIPTION Indicates that you want to enable the VMKernel Network Adapter for management traffic. #> [DscProperty()] [nullable[bool]] $ManagementTrafficEnabled <# .DESCRIPTION Indicates that the VMKernel Network Adapter is enabled for Fault Tolerance (FT) logging. #> [DscProperty()] [nullable[bool]] $FaultToleranceLoggingEnabled <# .DESCRIPTION Indicates that you want to use the VMKernel Network Adapter for VMotion. #> [DscProperty()] [nullable[bool]] $VMotionEnabled <# .DESCRIPTION Indicates that Virtual SAN traffic is enabled on this VMKernel Network Adapter. #> [DscProperty()] [nullable[bool]] $VsanTrafficEnabled hidden [string] $CouldNotCreateVMKernelNicMessage = "Cannot create VMKernel Network Adapter connected to Virtual Switch {0} and Port Group {1}. For more information: {2}" hidden [string] $CouldNotUpdateVMKernelNicMessage = "Cannot update VMKernel Network Adapter {0}. For more information: {1}" hidden [string] $CouldNotRemoveVMKernelNicMessage = "Cannot remove VMKernel Network Adapter {0}. For more information: {1}" <# .DESCRIPTION Retrieves the VMKernel Network Adapter connected to the specified Port Group and Virtual Switch and available on the specified VMHost from the server if it exists, otherwise returns $null. #> [PSObject] GetVMHostNetworkAdapter($virtualSwitch) { if ($null -eq $virtualSwitch) { <# If the Virtual Switch is $null, it means that Ensure was set to 'Absent' and the VMKernel Network Adapter does not exist for the specified Virtual Switch. #> return $null } return Get-VMHostNetworkAdapter -Server $this.Connection -PortGroup $this.PortGroupName -VirtualSwitch $virtualSwitch -VMHost $this.VMHost -VMKernel -ErrorAction SilentlyContinue } <# .DESCRIPTION Checks if the passed VMKernel Network Adapter IPv6 array needs to be updated. #> [bool] ShouldUpdateIPv6($ipv6) { $currentIPv6 = @() foreach ($ip in $ipv6) { <# The IPs on the server are of type 'IPv6Address' so they need to be converted to string before being passed to ShouldUpdateArraySetting method. #> $currentIPv6 += $ip.ToString() } <# The default IPv6 array contains one element, so when empty array is passed no update should be performed. #> if ($null -ne $this.IPv6 -and ($this.IPv6.Length -eq 0 -and $currentIPv6.Length -eq 1)) { return $false } return $this.ShouldUpdateArraySetting('IPv6', $currentIPv6, $this.IPv6) } <# .DESCRIPTION Checks if the passed VMKernel Network Adapter needs be updated based on the specified properties. #> [bool] ShouldUpdateVMHostNetworkAdapter($vmHostNetworkAdapter) { $shouldUpdateVMHostNetworkAdapter = @( $this.ShouldUpdateDscResourceSetting('IP', [string] $vmHostNetworkAdapter.IP, $this.IP), $this.ShouldUpdateDscResourceSetting('SubnetMask', [string] $vmHostNetworkAdapter.SubnetMask, $this.SubnetMask), $this.ShouldUpdateDscResourceSetting('Mac', [string] $vmHostNetworkAdapter.Mac, $this.Mac), $this.ShouldUpdateDscResourceSetting('Dhcp', $vmHostNetworkAdapter.DhcpEnabled, $this.Dhcp), $this.ShouldUpdateDscResourceSetting('AutomaticIPv6', $vmHostNetworkAdapter.AutomaticIPv6, $this.AutomaticIPv6), $this.ShouldUpdateIPv6($vmHostNetworkAdapter.IPv6), $this.ShouldUpdateDscResourceSetting('IPv6ThroughDhcp', $vmHostNetworkAdapter.IPv6ThroughDhcp, $this.IPv6ThroughDhcp), $this.ShouldUpdateDscResourceSetting('Mtu', $vmHostNetworkAdapter.Mtu, $this.Mtu), $this.ShouldUpdateDscResourceSetting('IPv6Enabled', $vmHostNetworkAdapter.IPv6Enabled, $this.IPv6Enabled), $this.ShouldUpdateDscResourceSetting('ManagementTrafficEnabled', $vmHostNetworkAdapter.ManagementTrafficEnabled, $this.ManagementTrafficEnabled), $this.ShouldUpdateDscResourceSetting('FaultToleranceLoggingEnabled', $vmHostNetworkAdapter.FaultToleranceLoggingEnabled, $this.FaultToleranceLoggingEnabled), $this.ShouldUpdateDscResourceSetting('VMotionEnabled', $vmHostNetworkAdapter.VMotionEnabled, $this.VMotionEnabled), $this.ShouldUpdateDscResourceSetting('VsanTrafficEnabled', $vmHostNetworkAdapter.VsanTrafficEnabled, $this.VsanTrafficEnabled) ) return ($shouldUpdateVMHostNetworkAdapter -Contains $true) } <# .DESCRIPTION Returns the populated VMKernel Network Adapter parameters. #> [hashtable] GetVMHostNetworkAdapterParams() { $vmHostNetworkAdapterParams = @{} $vmHostNetworkAdapterParams.Confirm = $false $vmHostNetworkAdapterParams.ErrorAction = 'Stop' if (![string]::IsNullOrEmpty($this.IP)) { $vmHostNetworkAdapterParams.IP = $this.IP } if (![string]::IsNullOrEmpty($this.SubnetMask)) { $vmHostNetworkAdapterParams.SubnetMask = $this.SubnetMask } if (![string]::IsNullOrEmpty($this.Mac)) { $vmHostNetworkAdapterParams.Mac = $this.Mac } if ($null -ne $this.AutomaticIPv6) { $vmHostNetworkAdapterParams.AutomaticIPv6 = $this.AutomaticIPv6 } if ($null -ne $this.IPv6) { $vmHostNetworkAdapterParams.IPv6 = $this.IPv6 } if ($null -ne $this.IPv6ThroughDhcp) { $vmHostNetworkAdapterParams.IPv6ThroughDhcp = $this.IPv6ThroughDhcp } if ($null -ne $this.Mtu) { $vmHostNetworkAdapterParams.Mtu = $this.Mtu } if ($null -ne $this.ManagementTrafficEnabled) { $vmHostNetworkAdapterParams.ManagementTrafficEnabled = $this.ManagementTrafficEnabled } if ($null -ne $this.FaultToleranceLoggingEnabled) { $vmHostNetworkAdapterParams.FaultToleranceLoggingEnabled = $this.FaultToleranceLoggingEnabled } if ($null -ne $this.VMotionEnabled) { $vmHostNetworkAdapterParams.VMotionEnabled = $this.VMotionEnabled } if ($null -ne $this.VsanTrafficEnabled) { $vmHostNetworkAdapterParams.VsanTrafficEnabled = $this.VsanTrafficEnabled } return $vmHostNetworkAdapterParams } <# .DESCRIPTION Creates a new VMKernel Network Adapter connected to the specified Virtual Switch and Port Group for the specified VMHost. If the Port Id is specified, the Port Group is ignored and only the Port Id is passed to the cmdlet. #> [PSObject] AddVMHostNetworkAdapter($virtualSwitch, $portId) { $vmHostNetworkAdapterParams = $this.GetVMHostNetworkAdapterParams() $vmHostNetworkAdapterParams.Server = $this.Connection $vmHostNetworkAdapterParams.VMHost = $this.VMHost $vmHostNetworkAdapterParams.VirtualSwitch = $virtualSwitch if ($null -ne $portId) { $vmHostNetworkAdapterParams.PortId = $portId } else { $vmHostNetworkAdapterParams.PortGroup = $this.PortGroupName } try { return New-VMHostNetworkAdapter @vmHostNetworkAdapterParams } catch { throw ($this.CouldNotCreateVMKernelNicMessage -f $virtualSwitch.Name, $this.PortGroupName, $_.Exception.Message) } } <# .DESCRIPTION Updates the VMKernel Network Adapter with the specified properties. #> [void] UpdateVMHostNetworkAdapter($vmHostNetworkAdapter) { $vmHostNetworkAdapterParams = $this.GetVMHostNetworkAdapterParams() <# IPv6 should only be passed to the cmdlet if an update needs to be performed. Otherwise the following error occurs when passing the same array: 'Address already exists in the config.' #> if (!$this.ShouldUpdateIPv6($vmHostNetworkAdapter.IPv6)) { $vmHostNetworkAdapterParams.Remove('IPv6') } <# Both Dhcp and IPv6Enabled are applicable only for the Update operation, so they are not populated in the GetVMHostNetworkAdapterParams() which is used for Create and Update operations. #> if ($null -ne $this.Dhcp) { $vmHostNetworkAdapterParams.Dhcp = $this.Dhcp <# IP and SubnetMask parameters are mutually exclusive with Dhcp so they should be removed from the parameters hashtable before calling Set-VMHostNetworkAdapter cmdlet. #> $vmHostNetworkAdapterParams.Remove('IP') $vmHostNetworkAdapterParams.Remove('SubnetMask') } if ($null -ne $this.IPv6Enabled) { $vmHostNetworkAdapterParams.IPv6Enabled = $this.IPv6Enabled if (!$this.IPv6Enabled) { <# If the value of IPv6Enabled is $false, other IPv6 settings cannot be specified. #> $vmHostNetworkAdapterParams.Remove('AutomaticIPv6') $vmHostNetworkAdapterParams.Remove('IPv6ThroughDhcp') $vmHostNetworkAdapterParams.Remove('IPv6') } } try { $vmHostNetworkAdapter | Set-VMHostNetworkAdapter @vmHostNetworkAdapterParams } catch { throw ($this.CouldNotUpdateVMKernelNicMessage -f $vmHostNetworkAdapter.Name, $_.Exception.Message) } } <# .DESCRIPTION Removes the VMKernel Network Adapter connected to the specified Virtual Switch and Port Group for the specified VMHost. #> [void] RemoveVMHostNetworkAdapter($vmHostNetworkAdapter) { try { Remove-VMHostNetworkAdapter -Nic $vmHostNetworkAdapter -Confirm:$false -ErrorAction Stop } catch { throw ($this.CouldNotRemoveVMKernelNicMessage -f $vmHostNetworkAdapter.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the VMKernel Network Adapter from the server. #> [void] PopulateResult($vmHostNetworkAdapter, $result) { $result.Server = $this.Connection.Name $result.VMHostName = $this.VMHost.Name if ($null -ne $vmHostNetworkAdapter) { $result.PortGroupName = $vmHostNetworkAdapter.PortGroupName $result.Ensure = [Ensure]::Present $result.IP = $vmHostNetworkAdapter.IP $result.SubnetMask = $vmHostNetworkAdapter.SubnetMask $result.Mac = $vmHostNetworkAdapter.Mac $result.AutomaticIPv6 = $vmHostNetworkAdapter.AutomaticIPv6 $result.IPv6 = $vmHostNetworkAdapter.IPv6 $result.IPv6ThroughDhcp = $vmHostNetworkAdapter.IPv6ThroughDhcp $result.Mtu = $vmHostNetworkAdapter.Mtu $result.Dhcp = $vmHostNetworkAdapter.DhcpEnabled $result.IPv6Enabled = $vmHostNetworkAdapter.IPv6Enabled $result.ManagementTrafficEnabled = $vmHostNetworkAdapter.ManagementTrafficEnabled $result.FaultToleranceLoggingEnabled = $vmHostNetworkAdapter.FaultToleranceLoggingEnabled $result.VMotionEnabled = $vmHostNetworkAdapter.VMotionEnabled $result.VsanTrafficEnabled = $vmHostNetworkAdapter.VsanTrafficEnabled } else { $result.PortGroupName = $this.PortGroupName $result.Ensure = [Ensure]::Absent $result.IP = $this.IP $result.SubnetMask = $this.SubnetMask $result.Mac = $this.Mac $result.AutomaticIPv6 = $this.AutomaticIPv6 $result.IPv6 = $this.IPv6 $result.IPv6ThroughDhcp = $this.IPv6ThroughDhcp $result.Mtu = $this.Mtu $result.Dhcp = $this.Dhcp $result.IPv6Enabled = $this.IPv6Enabled $result.ManagementTrafficEnabled = $this.ManagementTrafficEnabled $result.FaultToleranceLoggingEnabled = $this.FaultToleranceLoggingEnabled $result.VMotionEnabled = $this.VMotionEnabled $result.VsanTrafficEnabled = $this.VsanTrafficEnabled } } } class VMHostVssBaseDSC : VMHostNetworkBaseDSC { <# .DESCRIPTION Value indicating if the VSS should be Present or Absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION The name of the VSS. #> [DscProperty(Key)] [string] $VssName <# .DESCRIPTION Returns the desired virtual switch if it is present on the server otherwise returns $null. #> [PSObject] GetVss() { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.vmHostNetworkSystem.UpdateViewData('NetworkInfo.Vswitch') return ($this.vmHostNetworkSystem.NetworkInfo.Vswitch | Where-Object { $_.Name -eq $this.VssName }) } } class VMHostVssPortGroupBaseDSC : VMHostEntityBaseDSC { <# .DESCRIPTION Name of the the Port Group. #> [DscProperty(Key)] [string] $Name <# .DESCRIPTION Value indicating if the Port Group should be Present or Absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION The Network System of the specified VMHost. #> hidden [PSObject] $VMHostNetworkSystem hidden [string] $CouldNotRetrievePortGroupMessage = "Could not retrieve Virtual Port Group {0} of VMHost {1}. For more information: {2}" hidden [string] $CouldNotRetrieveNetworkSystemMessage = "Could not retrieve the Network System of VMHost {0}. For more information: {1}" <# .DESCRIPTION Retrieves the Virtual Port Group with the specified name from the server if it exists. The Virtual Port Group must be a Standard Virtual Port Group. If the Virtual Port Group does not exist and Ensure is set to 'Absent', $null is returned. Otherwise it throws an exception. #> [PSObject] GetVirtualPortGroup() { if ($this.Ensure -eq [Ensure]::Absent) { return $null } else { try { $virtualPortGroup = Get-VirtualPortGroup -Server $this.Connection -Name $this.Name -VMHost $this.VMHost -Standard -ErrorAction Stop return $virtualPortGroup } catch { throw ($this.CouldNotRetrievePortGroupMessage -f $this.Name, $this.VMHost.Name, $_.Exception.Message) } } } <# .DESCRIPTION Retrieves the Network System of the specified VMHost. #> [void] GetVMHostNetworkSystem() { try { $this.VMHostNetworkSystem = Get-View -Server $this.Connection -Id $this.VMHost.ExtensionData.ConfigManager.NetworkSystem -ErrorAction Stop } catch { throw ($this.CouldNotRetrieveNetworkSystemMessage -f $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the specified Policy Setting. If the Inherited Setting is passed and set to $true, the Policy Setting should not be populated because "Parameters of the form "XXX" and "InheritXXX" are mutually exclusive." If the Inherited Setting is set to $false, both parameters can be populated. #> [void] PopulatePolicySetting($policyParams, $policySettingName, $policySetting, $policySettingInheritedName, $policySettingInherited) { if ($null -ne $policySetting) { if ($null -eq $policySettingInherited -or !$policySettingInherited) { $policyParams.$policySettingName = $policySetting } } if ($null -ne $policySettingInherited) { $policyParams.$policySettingInheritedName = $policySettingInherited } } } [DscResource()] class Datacenter : InventoryBaseDSC { [void] Set() { try { $this.ConnectVIServer() $datacenterLocation = $this.GetInventoryItemLocation() $datacenter = $this.GetDatacenter($datacenterLocation) if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $datacenter) { $this.AddDatacenter($datacenterLocation) } } else { if ($null -ne $datacenter) { $this.RemoveDatacenter($datacenter) } } } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $datacenterLocation = $this.GetInventoryItemLocation() $datacenter = $this.GetDatacenter($datacenterLocation) if ($this.Ensure -eq [Ensure]::Present) { return ($null -ne $datacenter) } else { return ($null -eq $datacenter) } } finally { $this.DisconnectVIServer() } } [Datacenter] Get() { try { $result = [Datacenter]::new() $result.Server = $this.Server $result.Location = $this.Location $this.ConnectVIServer() $datacenterLocation = $this.GetInventoryItemLocation() $datacenter = $this.GetDatacenter($datacenterLocation) $this.PopulateResult($datacenter, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Returns the Datacenter from the specified Location if it exists, otherwise returns $null. #> [PSObject] GetDatacenter($datacenterLocation) { <# The client side filtering here is used so we can retrieve only the Datacenter which is located directly below the found Folder Location because Get-Datacenter searches recursively and can return more than one Datacenter located below the found Folder Location. #> return Get-Datacenter -Server $this.Connection -Name $this.Name -Location $datacenterLocation -ErrorAction SilentlyContinue | Where-Object { $_.ParentFolderId -eq $datacenterLocation.Id } } <# .DESCRIPTION Creates a new Datacenter with the specified properties at the specified Location. #> [void] AddDatacenter($datacenterLocation) { $datacenterParams = @{} $datacenterParams.Server = $this.Connection $datacenterParams.Name = $this.Name $datacenterParams.Location = $datacenterLocation $datacenterParams.Confirm = $false $datacenterParams.ErrorAction = 'Stop' try { New-Datacenter @datacenterParams } catch { throw "Cannot create Datacenter $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Removes the Datacenter from the specified Location. #> [void] RemoveDatacenter($datacenter) { $datacenterParams = @{} $datacenterParams.Server = $this.Connection $datacenterParams.Confirm = $false $datacenterParams.ErrorAction = 'Stop' try { $datacenter | Remove-Datacenter @datacenterParams } catch { throw "Cannot remove Datacenter $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Datacenter from the server. #> [void] PopulateResult($datacenter, $result) { if ($null -ne $datacenter) { $result.Name = $datacenter.Name $result.Ensure = [Ensure]::Present } else { $result.Name = $this.Name $result.Ensure = [Ensure]::Absent } } } [DscResource()] class DatacenterFolder : InventoryBaseDSC { [void] Set() { try { $this.ConnectVIServer() $datacenterFolderLocation = $this.GetInventoryItemLocation() $datacenterFolder = $this.GetDatacenterFolder($datacenterFolderLocation) if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $datacenterFolder) { $this.AddDatacenterFolder($datacenterFolderLocation) } } else { if ($null -ne $datacenterFolder) { $this.RemoveDatacenterFolder($datacenterFolder) } } } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $datacenterFolderLocation = $this.GetInventoryItemLocation() $datacenterFolder = $this.GetDatacenterFolder($datacenterFolderLocation) if ($this.Ensure -eq [Ensure]::Present) { return ($null -ne $datacenterFolder) } else { return ($null -eq $datacenterFolder) } } finally { $this.DisconnectVIServer() } } [DatacenterFolder] Get() { try { $result = [DatacenterFolder]::new() $result.Server = $this.Server $result.Location = $this.Location $this.ConnectVIServer() $datacenterFolderLocation = $this.GetInventoryItemLocation() $datacenterFolder = $this.GetDatacenterFolder($datacenterFolderLocation) $this.PopulateResult($datacenterFolder, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Returns the Datacenter Folder from the specified Location if it exists, otherwise returns $null. #> [PSObject] GetDatacenterFolder($datacenterFolderLocation) { <# The client side filtering here is used so we can retrieve only the Folder which is located directly below the found Folder Location because Get-Folder searches recursively and can return more than one Folder located below the found Folder Location. #> return Get-Folder -Server $this.Connection -Name $this.Name -Location $datacenterFolderLocation -ErrorAction SilentlyContinue | Where-Object { $_.ParentId -eq $datacenterFolderLocation.Id } } <# .DESCRIPTION Creates a new Datacenter Folder with the specified properties at the specified Location. #> [void] AddDatacenterFolder($datacenterFolderLocation) { $datacenterFolderParams = @{} $datacenterFolderParams.Server = $this.Connection $datacenterFolderParams.Name = $this.Name $datacenterFolderParams.Location = $datacenterFolderLocation $datacenterFolderParams.Confirm = $false $datacenterFolderParams.ErrorAction = 'Stop' try { New-Folder @datacenterFolderParams } catch { throw "Cannot create Datacenter Folder $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Removes the Datacenter Folder from the specified Location. #> [void] RemoveDatacenterFolder($datacenterFolder) { $datacenterFolderParams = @{} $datacenterFolderParams.Server = $this.Connection $datacenterFolderParams.Confirm = $false $datacenterFolderParams.ErrorAction = 'Stop' try { $datacenterFolder | Remove-Folder @datacenterFolderParams } catch { throw "Cannot remove Datacenter Folder $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Datacenter Folder from the server. #> [void] PopulateResult($datacenterFolder, $result) { if ($null -ne $datacenterFolder) { $result.Name = $datacenterFolder.Name $result.Ensure = [Ensure]::Present } else { $result.Name = $this.Name $result.Ensure = [Ensure]::Absent } } } [DscResource()] class DatastoreCluster : DatacenterInventoryBaseDSC { DatastoreCluster() { $this.InventoryItemFolderType = [FolderType]::Datastore } <# .DESCRIPTION Specifies the maximum I/O latency in milliseconds allowed before Storage DRS is triggered for the Datastore Cluster. Valid values are in the range of 5 to 100. If the value of IOLoadBalancing is $false, the setting for the I/O latency threshold is not applied. #> [DscProperty()] [nullable[int]] $IOLatencyThresholdMillisecond <# .DESCRIPTION Specifies whether I/O load balancing is enabled for the Datastore Cluster. If the value is $false, I/O load balancing is disabled and the settings for the I/O latency threshold and utilized space threshold are not applied. #> [DscProperty()] [nullable[bool]] $IOLoadBalanceEnabled <# .DESCRIPTION Specifies the Storage DRS automation level for the Datastore Cluster. Valid values are Disabled, Manual and FullyAutomated. #> [DscProperty()] [DrsAutomationLevel] $SdrsAutomationLevel = [DrsAutomationLevel]::Unset <# .DESCRIPTION Specifies the maximum percentage of consumed space allowed before Storage DRS is triggered for the Datastore Cluster. Valid values are in the range of 50 to 100. If the value of IOLoadBalancing is $false, the setting for the utilized space threshold is not applied. #> [DscProperty()] [nullable[int]] $SpaceUtilizationThresholdPercent hidden [string] $CreateDatastoreClusterMessage = "Creating Datastore Cluster {0} in Folder {1}." hidden [string] $ModifyDatastoreClusterMessage = "Modifying Datastore Cluster {0} configuration." hidden [string] $RemoveDatastoreClusterMessage = "Removing Datastore Cluster {0}." hidden [string] $CouldNotCreateDatastoreClusterMessage = "Could not create Datastore Cluster {0} in Folder {1}. For more information: {2}" hidden [string] $CouldNotModifyDatastoreClusterMessage = "Could not modify Datastore Cluster {0} configuration. For more information: {1}" hidden [string] $CouldNotRemoveDatastoreClusterMessage = "Could not remove Datastore Cluster {0}. For more information: {1}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $datastoreClusterLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $datastoreCluster = $this.GetDatastoreCluster($datastoreClusterLocation) if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $datastoreCluster) { $datastoreCluster = $this.NewDatastoreCluster($datastoreClusterLocation) } if ($this.ShouldModifyDatastoreCluster($datastoreCluster)) { $this.ModifyDatastoreCluster($datastoreCluster) } } else { if ($null -ne $datastoreCluster) { $this.RemoveDatastoreCluster($datastoreCluster) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $datastoreClusterLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $datastoreCluster = $this.GetDatastoreCluster($datastoreClusterLocation) $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $datastoreCluster) { $result = $false } else { $result = !$this.ShouldModifyDatastoreCluster($datastoreCluster) } } else { $result = ($null -eq $datastoreCluster) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [DatastoreCluster] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [DatastoreCluster]::new() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $datastoreClusterLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $datastoreCluster = $this.GetDatastoreCluster($datastoreClusterLocation) $this.PopulateResult($result, $datastoreCluster) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the Datastore Cluster with the specified name located in the specified Folder if it exists. #> [PSObject] GetDatastoreCluster($datastoreClusterLocation) { $getDatastoreClusterParams = @{ Server = $this.Connection Name = $this.Name Location = $datastoreClusterLocation ErrorAction = 'SilentlyContinue' Verbose = $false } $whereObjectParams = @{ FilterScript = { $_.ExtensionData.Parent -eq $datastoreClusterLocation.ExtensionData.MoRef } } <# Multiple Datastore Clusters with the same name can be present in a Datacenter. So we need to filter by the direct Parent Folder of the Datastore Cluster to retrieve the desired one. #> return Get-DatastoreCluster @getDatastoreClusterParams | Where-Object @whereObjectParams } <# .DESCRIPTION Checks if the specified Datastore Cluster configuration should be modified. #> [bool] ShouldModifyDatastoreCluster($datastoreCluster) { $shouldModifyDatastoreCluster = @( $this.ShouldUpdateDscResourceSetting( 'IOLatencyThresholdMillisecond', $datastoreCluster.IOLatencyThresholdMillisecond, $this.IOLatencyThresholdMillisecond ), $this.ShouldUpdateDscResourceSetting( 'IOLoadBalanceEnabled', $datastoreCluster.IOLoadBalanceEnabled, $this.IOLoadBalanceEnabled ), $this.ShouldUpdateDscResourceSetting( 'SdrsAutomationLevel', [string] $datastoreCluster.SdrsAutomationLevel, $this.SdrsAutomationLevel.ToString() ), $this.ShouldUpdateDscResourceSetting( 'SpaceUtilizationThresholdPercent', $datastoreCluster.SpaceUtilizationThresholdPercent, $this.SpaceUtilizationThresholdPercent ) ) return ($shouldModifyDatastoreCluster -Contains $true) } <# .DESCRIPTION Creates a new Datastore Cluster with the specified name located in the specified Folder. #> [PSObject] NewDatastoreCluster($datastoreClusterLocation) { $newDatastoreClusterParams = @{ Server = $this.Connection Name = $this.Name Location = $datastoreClusterLocation Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.CreateDatastoreClusterMessage, @($this.Name, $datastoreClusterLocation.Name)) return New-DatastoreCluster @newDatastoreClusterParams } catch { throw ($this.CouldNotCreateDatastoreClusterMessage -f $this.Name, $datastoreClusterLocation.Name, $_.Exception.Message) } } <# .DESCRIPTION Modifies the configuration of the specified Datastore Cluster. #> [void] ModifyDatastoreCluster($datastoreCluster) { $setDatastoreClusterParams = @{ Server = $this.Connection DatastoreCluster = $datastoreCluster Confirm = $false ErrorAction = 'Stop' Verbose = $false } if ($null -ne $this.IOLatencyThresholdMillisecond) { $setDatastoreClusterParams.IOLatencyThresholdMillisecond = $this.IOLatencyThresholdMillisecond } if ($null -ne $this.IOLoadBalanceEnabled) { $setDatastoreClusterParams.IOLoadBalanceEnabled = $this.IOLoadBalanceEnabled } if ($this.SdrsAutomationLevel -ne [DrsAutomationLevel]::Unset) { $setDatastoreClusterParams.SdrsAutomationLevel = $this.SdrsAutomationLevel.ToString() } if ($null -ne $this.SpaceUtilizationThresholdPercent) { $setDatastoreClusterParams.SpaceUtilizationThresholdPercent = $this.SpaceUtilizationThresholdPercent } try { $this.WriteLogUtil('Verbose', $this.ModifyDatastoreClusterMessage, @($datastoreCluster.Name)) Set-DatastoreCluster @setDatastoreClusterParams } catch { throw ($this.CouldNotModifyDatastoreClusterMessage -f $datastoreCluster.Name, $_.Exception.Message) } } <# .DESCRIPTION Removes the specified Datastore Cluster. #> [void] RemoveDatastoreCluster($datastoreCluster) { $removeDatastoreClusterParams = @{ Server = $this.Connection DatastoreCluster = $datastoreCluster Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.RemoveDatastoreClusterMessage, @($datastoreCluster.Name)) Remove-DatastoreCluster @removeDatastoreClusterParams } catch { throw ($this.CouldNotRemoveDatastoreClusterMessage -f $datastoreCluster.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $datastoreCluster) { $result.Server = $this.Server $result.Location = $this.Location $result.DatacenterName = $this.DatacenterName $result.DatacenterLocation = $this.DatacenterLocation if ($null -ne $datastoreCluster) { $result.Name = $datastoreCluster.Name $result.Ensure = [Ensure]::Present $result.IOLatencyThresholdMillisecond = $datastoreCluster.IOLatencyThresholdMillisecond $result.IOLoadBalanceEnabled = $datastoreCluster.IOLoadBalanceEnabled $result.SdrsAutomationLevel = $datastoreCluster.SdrsAutomationLevel.ToString() $result.SpaceUtilizationThresholdPercent = $datastoreCluster.SpaceUtilizationThresholdPercent } else { $result.Name = $this.Name $result.Ensure = [Ensure]::Absent $result.IOLatencyThresholdMillisecond = $this.IOLatencyThresholdMillisecond $result.IOLoadBalanceEnabled = $this.IOLoadBalanceEnabled $result.SdrsAutomationLevel = $this.SdrsAutomationLevel $result.SpaceUtilizationThresholdPercent = $this.SpaceUtilizationThresholdPercent } } } [DscResource()] class DatastoreClusterAddDatastore : BaseDSC { <# .DESCRIPTION Specifies the name of the Datacenter where the specified Datastore Cluster and Datastores are located. #> [DscProperty(Key)] [string] $DatacenterName <# .DESCRIPTION Specifies the location of the Datacenter where the specified Datastore Cluster and Datastores are located. The Root Folder of the Inventory is not part of the location. Empty location means that the Datacenter is in the Root Folder of the Inventory. The Folder names in the location are separated by '/'. Example Datacenter location: 'MyDatacentersFolderOne/MyDatacentersFolderTwo'. #> [DscProperty(Key)] [string] $DatacenterLocation <# .DESCRIPTION Specifies the name of the Datastore Cluster located in the Datacenter specified in 'DatacenterName' key property. #> [DscProperty(Key)] [string] $DatastoreClusterName <# .DESCRIPTION Specifies the location of the Datastore Cluster with name specified in 'DatastoreClusterName' key property in the Datacenter specified in 'DatacenterName' key property. Location consists of 0 or more Folders. Empty location means that the Datastore Cluster is located in the Datastore Folder of the Datacenter. The Root Folders of the Datacenter are not part of the location. Folder names in the location are separated by '/'. Example location for a Datastore Cluster: 'MyDatastoreClusterFolderOne/MyDatastoreClusterFolderTwo'. #> [DscProperty(Key)] [string] $DatastoreClusterLocation <# .DESCRIPTION Specifies the names of the Datastores that should be located in the specified Datastore Cluster. #> [DscProperty(Mandatory)] [string[]] $DatastoreNames <# .DESCRIPTION Specifies the instance of the 'InventoryUtil' class that is used for Inventory operations. #> hidden [InventoryUtil] $InventoryUtil hidden [string] $AddDatastoresToDatastoreClusterMessage = "Adding Datastores {0} to Datastore Cluster {1}." hidden [string] $CouldNotFindDatastoreMessage = "Could not find Datastore {0} in Datacenter {1}." hidden [string] $CouldNotAddDatastoresToDatastoreClusterMessage = "Could not add Datastores {0} to Datastore Cluster {1}. For more information: {2}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.InitInventoryUtil() $datacenter = $this.InventoryUtil.GetDatacenter($this.DatacenterName, $this.DatacenterLocation) $datacenterDatastoreFolderName = [FolderType]::Datastore.ToString() + 'Folder' $datastoreCluster = $this.InventoryUtil.GetDatastoreCluster( $this.DatastoreClusterName, $this.InventoryUtil.GetInventoryItemParent( $this.DatastoreClusterLocation, $datacenter, $datacenterDatastoreFolderName ) ) $datastores = $this.GetDatastores($datacenter) $datastoresToAddToDatastoreCluster = $this.GetDatastoresToAddToDatastoreCluster($datastoreCluster, $datastores) $this.AddDatastoresToDatastoreCluster($datastoreCluster, $datastoresToAddToDatastoreCluster) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.InitInventoryUtil() $datacenter = $this.InventoryUtil.GetDatacenter($this.DatacenterName, $this.DatacenterLocation) $datacenterDatastoreFolderName = [FolderType]::Datastore.ToString() + 'Folder' $datastoreCluster = $this.InventoryUtil.GetDatastoreCluster( $this.DatastoreClusterName, $this.InventoryUtil.GetInventoryItemParent( $this.DatastoreClusterLocation, $datacenter, $datacenterDatastoreFolderName ) ) $datastores = $this.GetDatastores($datacenter) $datastoresToAddToDatastoreCluster = $this.GetDatastoresToAddToDatastoreCluster($datastoreCluster, $datastores) $result = !($datastoresToAddToDatastoreCluster.Length -gt 0) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [DatastoreClusterAddDatastore] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [DatastoreClusterAddDatastore]::new() $this.InitInventoryUtil() $datacenter = $this.InventoryUtil.GetDatacenter($this.DatacenterName, $this.DatacenterLocation) $datacenterDatastoreFolderName = [FolderType]::Datastore.ToString() + 'Folder' $datastoreCluster = $this.InventoryUtil.GetDatastoreCluster( $this.DatastoreClusterName, $this.InventoryUtil.GetInventoryItemParent( $this.DatastoreClusterLocation, $datacenter, $datacenterDatastoreFolderName ) ) $datastores = $this.GetDatastoresInDatastoreCluster($datastoreCluster) $result.Server = $this.Server $result.DatacenterName = $datacenter.Name $result.DatacenterLocation = $this.DatacenterLocation $result.DatastoreClusterName = $datastoreCluster.Name $result.DatastoreClusterLocation = $this.DatastoreClusterLocation $result.DatastoreNames = $datastores | Select-Object -ExpandProperty Name return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Initializes an instance of the 'InventoryUtil' class. #> [void] InitInventoryUtil() { if ($null -eq $this.InventoryUtil) { $this.InventoryUtil = [InventoryUtil]::new($this.Connection, [Ensure]::Present) } } <# .DESCRIPTION Retrieves the Datastores with the specified names located in the specified Datacenter. #> [array] GetDatastores($datacenter) { $result = @() if ($this.DatastoreNames.Length -gt 0) { $getDatastoreParams = @{ Server = $this.Connection Name = $this.DatastoreNames Location = $datacenter ErrorAction = 'SilentlyContinue' Verbose = $false } $result = Get-Datastore @getDatastoreParams } if ($this.DatastoreNames.Length -gt $result.Length) { $notFoundDatastoreNames = $this.DatastoreNames | Where-Object -FilterScript { $result.Name -NotContains $_ } foreach ($notFoundDatastoreName in $notFoundDatastoreNames) { $this.WriteLogUtil('Warning', $this.CouldNotFindDatastoreMessage, @($notFoundDatastoreName, $datacenter.Name)) } } return $result } <# .DESCRIPTION Retrieves the Datastores located in the specified Datastore Cluster. #> [array] GetDatastoresInDatastoreCluster($datastoreCluster) { $getDatastoreParams = @{ Server = $this.Connection Location = $datastoreCluster ErrorAction = 'SilentlyContinue' Verbose = $false } return Get-Datastore @getDatastoreParams } <# .DESCRIPTION Retrieves only the Datastores that are still not added to the specified Datastore Cluster. #> [array] GetDatastoresToAddToDatastoreCluster($datastoreCluster, $datastores) { $whereObjectParams = @{ FilterScript = { $_.ParentFolderId -ne $datastoreCluster.Id } } return $datastores | Where-Object @whereObjectParams } <# .DESCRIPTION Adds the specified Datastores to the Datastore Cluster. #> [void] AddDatastoresToDatastoreCluster($datastoreCluster, $datastoresToAddToDatastoreCluster) { $moveDatastoreParams = @{ Server = $this.Connection Datastore = $datastoresToAddToDatastoreCluster Destination = $datastoreCluster Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.AddDatastoresToDatastoreClusterMessage, @( ($datastoresToAddToDatastoreCluster.Name -Join ', '), $datastoreCluster.Name )) Move-Datastore @moveDatastoreParams } catch { throw ( $this.CouldNotAddDatastoresToDatastoreClusterMessage -f @( ($datastoresToAddToDatastoreCluster.Name -Join ', '), $datastoreCluster.Name, $_.Exception.Message ) ) } } } [DscResource()] class DRSRule : BaseDSC { <# .DESCRIPTION Specifies the name of the DRS rule. #> [DscProperty(Key)] [string] $Name <# .DESCRIPTION Specifies the name of the Datacenter where the Cluster, for which the DRS rule applies, is located. #> [DscProperty(Key)] [string] $DatacenterName <# .DESCRIPTION Specifies the location of the Datacenter where the Cluster, for which the DRS rule applies, is located. The Root Folder of the Inventory is not part of the location. Empty location means that the Datacenter is located in the Root Folder of the Inventory. The Folder names in the location are separated by '/'. Example Datacenter location: 'MyDatacentersFolderOne/MyDatacentersFolderTwo'. #> [DscProperty(Key)] [string] $DatacenterLocation <# .DESCRIPTION Specifies the name of the Cluster for which the DRS rule applies. #> [DscProperty(Key)] [string] $ClusterName <# .DESCRIPTION Specifies the location of the Cluster, for which the DRS rule applies, located in the Datacenter specified in 'DatacenterName' key property. The Root Folders of the Datacenter are not part of the location. Empty location means that the Cluster is located in the Host Folder of the Datacenter. The Folder names in the location are separated by '/'. Example Cluster location: 'MyClusterFolderOne/MyClusterFolderTwo'. #> [DscProperty(Key)] [string] $ClusterLocation <# .DESCRIPTION Specifies the type of the DRS rule - affinity or anti-affinity. #> [DscProperty(Key)] [DRSRuleType] $DRSRuleType <# .DESCRIPTION Specifies the names of the virtual machines that are referenced by the DRS rule. #> [DscProperty(Mandatory)] [string[]] $VMNames <# .DESCRIPTION Specifies whether the DRS rule should be present or absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION Specifies whether the DRS rule is enabled or disabled for the specified Cluster. #> [DscProperty()] [nullable[bool]] $Enabled <# .DESCRIPTION Specifies the instance of the 'InventoryUtil' class that is used for Inventory operations. #> hidden [InventoryUtil] $InventoryUtil hidden [string] $CreateDrsRuleMessage = "Creating DRS Rule {0} for Cluster {1}." hidden [string] $ModifyDrsRuleMessage = "Modifying DRS rule {0} for Cluster {1}." hidden [string] $RemoveDrsRuleMessage = "Removing DRS rule {0} for Cluster {1}." hidden [string] $InvalidVMCountMessage = "At least 2 Virtual Machines should be specified." hidden [string] $CouldNotFindVMMessage = "Could not find Virtual Machine {0} in Cluster {1}." hidden [string] $CouldNotCreateDrsRuleMessage = "Could not create DRS rule {0} for Cluster {1}. For more information: {2}" hidden [string] $CouldNotModifyDrsRuleMessage = "Could not modify DRS rule {0} for Cluster {1}. For more information: {2}" hidden [string] $CouldNotRemoveDrsRuleMessage = "Could not remove DRS rule {0} for Cluster {1}. For more information: {2}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.InitInventoryUtil() $datacenter = $this.InventoryUtil.GetDatacenter($this.DatacenterName, $this.DatacenterLocation) $datacenterHostFolderName = [FolderType]::Host.ToString() + 'Folder' $cluster = $this.InventoryUtil.GetInventoryItem( $this.ClusterName, $this.InventoryUtil.GetInventoryItemParent( $this.ClusterLocation, $datacenter, $datacenterHostFolderName ) ) $drsRule = $this.GetDrsRule($cluster) if ($this.Ensure -eq [Ensure]::Present) { $virtualMachines = $this.GetVirtualMachines($cluster) if ($null -eq $drsRule) { $this.NewDrsRule($cluster, $virtualMachines) } else { $this.ModifyDrsRule($drsRule, $virtualMachines) } } else { if ($null -ne $drsRule) { $this.RemoveDrsRule($drsRule) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.InitInventoryUtil() $datacenter = $this.InventoryUtil.GetDatacenter($this.DatacenterName, $this.DatacenterLocation) $datacenterHostFolderName = [FolderType]::Host.ToString() + 'Folder' $cluster = $this.InventoryUtil.GetInventoryItem( $this.ClusterName, $this.InventoryUtil.GetInventoryItemParent( $this.ClusterLocation, $datacenter, $datacenterHostFolderName ) ) $drsRule = $this.GetDrsRule($cluster) $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $drsRule) { $result = $false } else { $result = !$this.ShouldModifyDrsRule($drsRule) } } else { $result = ($null -eq $drsRule) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [DRSRule] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [DRSRule]::new() $this.InitInventoryUtil() $datacenter = $this.InventoryUtil.GetDatacenter($this.DatacenterName, $this.DatacenterLocation) $datacenterHostFolderName = [FolderType]::Host.ToString() + 'Folder' $cluster = $this.InventoryUtil.GetInventoryItem( $this.ClusterName, $this.InventoryUtil.GetInventoryItemParent( $this.ClusterLocation, $datacenter, $datacenterHostFolderName ) ) $drsRule = $this.GetDrsRule($cluster) $result.DatacenterName = $datacenter.Name $result.ClusterName = $cluster.Name $this.PopulateResult($result, $drsRule) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Initializes an instance of the 'InventoryUtil' class. #> [void] InitInventoryUtil() { if ($null -eq $this.InventoryUtil) { $this.InventoryUtil = [InventoryUtil]::new($this.Connection, $this.Ensure) } } <# .DESCRIPTION Retrieves the DRS Rule with the speciifed name located in the specified Cluster. #> [PSObject] GetDrsRule($cluster) { $getDrsRuleParams = @{ Server = $this.Connection Name = $this.Name Cluster = $cluster ErrorAction = 'SilentlyContinue' Verbose = $false } return Get-DrsRule @getDrsRuleParams } <# .DESCRIPTION Retrieves the Virtual Machines with the specified names located in the specified Cluster. #> [array] GetVirtualMachines($cluster) { $result = @() if ($this.VMNames.Length -gt 0) { $getVMParams = @{ Server = $this.Connection Name = $this.VMNames Location = $cluster ErrorAction = 'SilentlyContinue' Verbose = $false } $result = Get-VM @getVMParams } if ($result.Length -lt 2) { throw $this.InvalidVMCountMessage } if ($this.VMNames.Length -gt $result.Length) { $notFoundVMNames = $this.VMNames | Where-Object -FilterScript { $result.Name -NotContains $_ } foreach ($notFoundVMName in $notFoundVMNames) { $this.WriteLogUtil('Warning', $this.CouldNotFindVMMessage, @($notFoundVMName, $cluster.Name)) } } return $result } <# .DESCRIPTION Retrieves the names of the Virtual Machines with the specified Ids. #> [string[]] GetVirtualMachineNames($vmIds) { $getVMParams = @{ Server = $this.Connection Id = $vmIds ErrorAction = 'SilentlyContinue' Verbose = $false } return Get-VM @getVMParams | Select-Object -ExpandProperty Name } <# .DESCRIPTION Checks if the specified DRS rule should be modified. #> [bool] ShouldModifyDrsRule($drsRule) { $shouldModifyDrsRule = @( $this.ShouldUpdateDscResourceSetting('Enabled', $drsRule.Enabled, $this.Enabled), $this.ShouldUpdateArraySetting('VMNames', $this.GetVirtualMachineNames($drsRule.VMIds), $this.VMNames) ) return ($shouldModifyDrsRule -Contains $true) } <# .DESCRIPTION Creates a new DRS rule with the specified name for the specified Cluster. #> [void] NewDrsRule($cluster, $virtualMachines) { $newDrsRuleParams = @{ Server = $this.Connection Name = $this.Name Cluster = $cluster KeepTogether = ($this.DRSRuleType -eq [DRSRuleType]::VMAffinity) VM = $virtualMachines Confirm = $false ErrorAction = 'Stop' Verbose = $false } if ($null -ne $this.Enabled) { $newDrsRuleParams.Enabled = $this.Enabled } try { $this.WriteLogUtil('Verbose', $this.CreateDrsRuleMessage, @($this.Name, $cluster.Name)) New-DrsRule @newDrsRuleParams } catch { throw ($this.CouldNotCreateDrsRuleMessage -f $this.Name, $cluster.Name, $_.Exception.Message) } } <# .DESCRIPTION Modifies the specified DRS rule. #> [void] ModifyDrsRule($drsRule, $virtualMachines) { $setDrsRuleParams = @{ Server = $this.Connection Rule = $drsRule VM = $virtualMachines Confirm = $false ErrorAction = 'Stop' Verbose = $false } if ($null -ne $this.Enabled) { $setDrsRuleParams.Enabled = $this.Enabled } try { $this.WriteLogUtil('Verbose', $this.ModifyDrsRuleMessage, @($drsRule.Name, $drsRule.Cluster.Name)) Set-DrsRule @setDrsRuleParams } catch { throw ($this.CouldNotModifyDrsRuleMessage -f $drsRule.Name, $drsRule.Cluster.Name, $_.Exception.Message) } } <# .DESCRIPTION Removes the specified DRS rule. #> [void] RemoveDrsRule($drsRule) { $removeDrsRuleParams = @{ Rule = $drsRule Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.RemoveDrsRuleMessage, @($drsRule.Name, $drsRule.Cluster.Name)) Remove-DrsRule @removeDrsRuleParams } catch { throw ($this.CouldNotRemoveDrsRuleMessage -f $drsRule.Name, $drsRule.Cluster.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $drsRule) { $result.Server = $this.Server $result.DatacenterLocation = $this.DatacenterLocation $result.ClusterLocation = $this.ClusterLocation if ($null -ne $drsRule) { $result.Name = $drsRule.Name $result.DRSRuleType = [string] $drsRule.Type $result.VMNames = $this.GetVirtualMachineNames($drsRule.VMIds) $result.Ensure = [Ensure]::Present $result.Enabled = $drsRule.Enabled } else { $result.Name = $this.Name $result.DRSRuleType = $this.DRSRuleType $result.VMNames = $this.VMNames $result.Ensure = [Ensure]::Absent $result.Enabled = $this.Enabled } } } [DscResource()] class Folder : DatacenterInventoryBaseDSC { <# .DESCRIPTION The type of Root Folder in the Datacenter in which the Folder is located. Possible values are VM, Network, Datastore, Host. #> [DscProperty(Key)] [FolderType] $FolderType [void] Set() { try { $this.ConnectVIServer() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.FolderType)Folder" $folderLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $folder = $this.GetInventoryItem($folderLocation) if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $folder) { $this.AddFolder($folderLocation) } } else { if ($null -ne $folder) { $this.RemoveFolder($folder) } } } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.FolderType)Folder" $folderLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $folder = $this.GetInventoryItem($folderLocation) if ($this.Ensure -eq [Ensure]::Present) { return ($null -ne $folder) } else { return ($null -eq $folder) } } finally { $this.DisconnectVIServer() } } [Folder] Get() { try { $result = [Folder]::new() $result.Server = $this.Server $result.Location = $this.Location $result.DatacenterName = $this.DatacenterName $result.DatacenterLocation = $this.DatacenterLocation $result.FolderType = $this.FolderType $this.ConnectVIServer() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.FolderType)Folder" $folderLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $folder = $this.GetInventoryItem($folderLocation) $this.PopulateResult($folder, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Creates a new Folder with the specified properties at the specified Location. #> [void] AddFolder($folderLocation) { $folderParams = @{} $folderParams.Server = $this.Connection $folderParams.Name = $this.Name $folderParams.Location = $folderLocation $folderParams.Confirm = $false $folderParams.ErrorAction = 'Stop' try { New-Folder @folderParams } catch { throw "Cannot create Folder $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Removes the Folder from the specified Location. #> [void] RemoveFolder($folder) { $folderParams = @{} $folderParams.Server = $this.Connection $folderParams.Confirm = $false $folderParams.ErrorAction = 'Stop' try { $folder | Remove-Folder @folderParams } catch { throw "Cannot remove Folder $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Folder from the server. #> [void] PopulateResult($folder, $result) { if ($null -ne $folder) { $result.Name = $folder.Name $result.Ensure = [Ensure]::Present } else { $result.Name = $this.Name $result.Ensure = [Ensure]::Absent } } } [DscResource()] class NfsUser : VMHostEntityBaseDSC { <# .DESCRIPTION Specifies the Nfs User name used for Kerberos authentication. #> [DscProperty(Key)] [string] $Name <# .DESCRIPTION Specifies the Nfs User password used for Kerberos authentication. #> [DscProperty()] [string] $Password <# .DESCRIPTION Specifies whether the Nfs User should be present or absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION Specifies whether to change the password of the Nfs User. When the property is not specified or is $false, it is ignored. If the property is $true and the Nfs User exists, the password of the Nfs User is changed. #> [DscProperty()] [nullable[bool]] $Force hidden [string] $CreateNfsUserMessage = "Creating Nfs User {0} on VMHost {1}." hidden [string] $ChangeNfsUserPasswordMessage = "Changing Nfs User {0} password on VMHost {1}." hidden [string] $RemoveNfsUserMessage = "Removing Nfs User {0} from VMHost {1}." hidden [string] $CouldNotCreateNfsUserMessage = "Could not create Nfs User {0} on VMHost {1}. For more information: {2}" hidden [string] $CouldNotChangeNfsUserPasswordMessage = "Could not change Nfs User {0} password on VMHost {1}. For more information: {2}" hidden [string] $CouldNotRemoveNfsUserMessage = "Could not remove Nfs User {0} from VMHost {1}. For more information: {2}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $nfsUser = $this.GetNfsUser() if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $nfsUser) { $this.NewNfsUser() } else { $this.ChangeNfsUserPassword($nfsUser) } } else { if ($null -ne $nfsUser) { $this.RemoveNfsUser($nfsUser) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $nfsUser = $this.GetNfsUser() $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $nfsUser) { $result = $false } else { $result = !($null -ne $this.Force -and $this.Force) } } else { $result = ($null -eq $nfsUser) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [NfsUser] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [NfsUser]::new() $this.RetrieveVMHost() $nfsUser = $this.GetNfsUser() $this.PopulateResult($result, $nfsUser) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the Nfs User with the specified name from the VMHost if it exists. #> [PSObject] GetNfsUser() { <# The Verbose logic here is needed to suppress the Verbose output of the Import-Module cmdlet when importing the 'VMware.VimAutomation.Storage' Module. #> $savedVerbosePreference = $global:VerbosePreference $global:VerbosePreference = 'SilentlyContinue' $nfsUser = Get-NfsUser -Server $this.Connection -Username $this.Name -VMHost $this.VMHost -ErrorAction SilentlyContinue -Verbose:$false $global:VerbosePreference = $savedVerbosePreference return $nfsUser } <# .DESCRIPTION Creates a new Nfs User with the specified name and password on the VMHost. #> [void] NewNfsUser() { $newNfsUserParams = @{ Server = $this.Connection Username = $this.Name VMHost = $this.VMHost Password = $this.Password Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.CreateNfsUserMessage, @($this.Name, $this.VMHost.Name)) New-NfsUser @newNfsUserParams } catch { throw ($this.CouldNotCreateNfsUserMessage -f $this.Name, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Changes the password of the specified Nfs User on the VMHost. #> [void] ChangeNfsUserPassword($nfsUser) { $setNfsUserParams = @{ Server = $this.Connection NfsUser = $nfsUser Password = $this.Password Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.ChangeNfsUserPasswordMessage, @($nfsUser.Username, $this.VMHost.Name)) Set-NfsUser @setNfsUserParams } catch { throw ($this.CouldNotChangeNfsUserPasswordMessage -f $nfsUser.Username, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Removes the specified Nfs User from the VMHost. #> [void] RemoveNfsUser($nfsUser) { $removeNfsUserParams = @{ NfsUser = $nfsUser Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.RemoveNfsUserMessage, @($nfsUser.Username, $this.VMHost.Name)) Remove-NfsUser @removeNfsUserParams } catch { throw ($this.CouldNotRemoveNfsUserMessage -f $nfsUser.Username, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $nfsUser) { $result.Server = $this.Connection.Name $result.VMHostName = $this.VMHost.Name $result.Force = $this.Force if ($null -ne $nfsUser) { $result.Name = $nfsUser.Username $result.Ensure = [Ensure]::Present } else { $result.Name = $this.Name $result.Ensure = [Ensure]::Absent } } } [DscResource()] class PowerCLISettings { <# .DESCRIPTION Specifies the scope on which the PowerCLI Settings will be applied. LCM is the only possible value for the Settings Scope. #> [DscProperty(Key)] [PowerCLISettingsScope] $SettingsScope <# .DESCRIPTION Specifies the proxy policy for the connection through which Customer Experience Improvement Program (CEIP) data is sent to VMware. #> [DscProperty()] [ProxyPolicy] $CEIPDataTransferProxyPolicy = [ProxyPolicy]::Unset <# .DESCRIPTION Specifies the server connection mode. #> [DscProperty()] [DefaultVIServerMode] $DefaultVIServerMode = [DefaultVIServerMode]::Unset <# .DESCRIPTION Indicates whether you want to see warnings about deprecated elements. #> [DscProperty()] [nullable[bool]] $DisplayDeprecationWarnings <# .DESCRIPTION Define the action to take when an attempted connection to a server fails due to a certificate error. #> [DscProperty()] [BadCertificateAction] $InvalidCertificateAction = [BadCertificateAction]::Unset <# .DESCRIPTION Specifies if PowerCLI should send anonymous usage information to VMware. #> [DscProperty()] [nullable[bool]] $ParticipateInCeip <# .DESCRIPTION Specifies whether VMware PowerCLI uses a system proxy server to connect to the vCenter Server system. #> [DscProperty()] [ProxyPolicy] $ProxyPolicy = [ProxyPolicy]::Unset <# .DESCRIPTION Defines the timeout for Web operations. The default value is 300 sec. #> [DscProperty()] [nullable[int]] $WebOperationTimeoutSeconds hidden [string] $Scope = "User" [void] Set() { $this.ImportRequiredModules() $powerCLIConfigurationProperties = $this.GetPowerCLIConfigurationProperties() $commandName = 'Set-PowerCLIConfiguration' $namesOfPowerCLIConfigurationProperties = $powerCLIConfigurationProperties.Keys <# For testability we use this function to construct the Set-PowerCLIConfiguration cmdlet instead of using splatting and passing the cmdlet parameters as hashtable. At the moment Pester does not allow to pass hashtable in the ParameterFilter property of the Assert-MockCalled function. There is an open issue in GitHub: (https://github.com/pester/Pester/issues/862) describing the problem in details. #> $constructedCommand = $this.ConstructCommandWithParameters($commandName, $powerCLIConfigurationProperties, $namesOfPowerCLIConfigurationProperties) Invoke-Expression -Command $constructedCommand } [bool] Test() { $this.ImportRequiredModules() $powerCLICurrentConfiguration = Get-PowerCLIConfiguration -Scope $this.Scope $powerCLIDesiredConfiguration = $this.GetPowerCLIConfigurationProperties() return $this.Equals($powerCLICurrentConfiguration, $powerCLIDesiredConfiguration) } [PowerCLISettings] Get() { $this.ImportRequiredModules() $result = [PowerCLISettings]::new() $powerCLICurrentConfiguration = Get-PowerCLIConfiguration -Scope $this.Scope $this.PopulateResult($powerCLICurrentConfiguration, $result) return $result } <# .DESCRIPTION Imports the needed VMware Modules. #> [void] ImportRequiredModules() { $savedVerbosePreference = $global:VerbosePreference $global:VerbosePreference = 'SilentlyContinue' Import-Module -Name VMware.VimAutomation.Core $global:VerbosePreference = $savedVerbosePreference } <# .DESCRIPTION Returns all passed PowerCLI configuration properties as a hashtable. #> [hashtable] GetPowerCLIConfigurationProperties() { $powerCLIConfigurationProperties = @{} # Adds the Default Scope to the hashtable. $powerCLIConfigurationProperties.Add("Scope", $this.Scope) if ($this.CEIPDataTransferProxyPolicy -ne [ProxyPolicy]::Unset -and $this.ParticipateInCeip -eq $true) { $powerCLIConfigurationProperties.Add("CEIPDataTransferProxyPolicy", $this.CEIPDataTransferProxyPolicy) } if ($this.DefaultVIServerMode -ne [DefaultVIServerMode]::Unset) { $powerCLIConfigurationProperties.Add("DefaultVIServerMode", $this.DefaultVIServerMode) } if ($null -ne $this.DisplayDeprecationWarnings) { $powerCLIConfigurationProperties.Add("DisplayDeprecationWarnings", $this.DisplayDeprecationWarnings) } if ($this.InvalidCertificateAction -ne [BadCertificateAction]::Unset) { $powerCLIConfigurationProperties.Add("InvalidCertificateAction", $this.InvalidCertificateAction) } if ($null -ne $this.ParticipateInCeip) { $powerCLIConfigurationProperties.Add("ParticipateInCeip", $this.ParticipateInCeip) } if ($this.ProxyPolicy -ne [ProxyPolicy]::Unset) { $powerCLIConfigurationProperties.Add("ProxyPolicy", $this.ProxyPolicy) } if ($null -ne $this.WebOperationTimeoutSeconds) { $powerCLIConfigurationProperties.Add("WebOperationTimeoutSeconds", $this.WebOperationTimeoutSeconds) } return $powerCLIConfigurationProperties } <# .DESCRIPTION Constructs the Set-PowerCLIConfiguration cmdlet with the passed properties. This function is used instead of splatting because at the moment Pester does not allow to pass hashtable in the ParameterFilter property of the Assert-MockCalled function. There is an open issue in GitHub: (https://github.com/pester/Pester/issues/862) describing the problem in details. So with this function we can successfully test which properties are passed to the Set-PowerCLIConfiguration cmdlet. #> [string] ConstructCommandWithParameters($commandName, $properties, $namesOfProperties) { $constructedCommand = [System.Text.StringBuilder]::new() # Adds the command name to the constructed command. [void]$constructedCommand.Append("$commandName ") # For every property name we add the property value with the following syntax: '-Property Value'. foreach ($propertyName in $namesOfProperties) { $propertyValue = $properties.$propertyName <# For bool values we need to add another '$' sign so the value can be evaluated to bool. So we check the type of the value and if it is a boolean we add another '$' sign, because without it the value will not be evaluated to boolean and instead it will be evaluated to string which will cause an exception of mismatching types. #> if ($propertyValue.GetType().Name -eq 'Boolean') { [void]$constructedCommand.Append("-$propertyName $") [void]$constructedCommand.Append("$propertyValue ") } else { [void]$constructedCommand.Append("-$propertyName $propertyValue ") } } # Adds the confirm:$false to the command to ignore the confirmation. [void]$constructedCommand.Append("-Confirm:`$false") # Converts the StringBuilder to String and returns the result. return $constructedCommand.ToString() } <# .DESCRIPTION Returns a boolean value indicating if the current PowerCLI Configuration is equal to the Desired Configuration. #> [bool] Equals($powerCLICurrentConfiguration, $powerCLIDesiredConfiguration) { foreach ($key in $powerCLIDesiredConfiguration.Keys) { <# Currently works only for properties which are numbers, strings and enums. For more complex types like Hashtable the logic needs to be modified to work correctly. #> if ($powerCLIDesiredConfiguration.$key.ToString() -ne $powerCLICurrentConfiguration.$key.ToString()) { return $false } } return $true } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the PowerCLI settings from the server. #> [void] PopulateResult($powerCLICurrentConfiguration, $result) { $result.SettingsScope = $this.SettingsScope $result.CEIPDataTransferProxyPolicy = if ($null -ne $powerCLICurrentConfiguration.CEIPDataTransferProxyPolicy) { $powerCLICurrentConfiguration.CEIPDataTransferProxyPolicy.ToString() } else { [ProxyPolicy]::Unset } $result.DefaultVIServerMode = if ($null -ne $powerCLICurrentConfiguration.DefaultVIServerMode) { $powerCLICurrentConfiguration.DefaultVIServerMode.ToString() } else { [DefaultVIServerMode]::Unset } $result.DisplayDeprecationWarnings = $powerCLICurrentConfiguration.DisplayDeprecationWarnings $result.InvalidCertificateAction = if ($null -ne $powerCLICurrentConfiguration.InvalidCertificateAction) { $powerCLICurrentConfiguration.InvalidCertificateAction.ToString() } else { [BadCertificateAction]::Unset } $result.ParticipateInCeip = $powerCLICurrentConfiguration.ParticipateInCEIP $result.ProxyPolicy = if ($null -ne $powerCLICurrentConfiguration.ProxyPolicy) { $powerCLICurrentConfiguration.ProxyPolicy.ToString() } else { [ProxyPolicy]::Unset } $result.WebOperationTimeoutSeconds = $powerCLICurrentConfiguration.WebOperationTimeoutSeconds } } <# .NOTES vCenterSettings inherits BasevSphereConnection instead of BaseDSC because it is a specific case where the resource does not have it's own DSC key property. That's why were define a $Server property here in order to use it as a key. #> [DscResource()] class vCenterSettings : BasevSphereConnection { <# .DESCRIPTION Name of the Server we are trying to connect to. The Server must be a vCenter. #> [DscProperty(Key)] [string] $Server <# .DESCRIPTION Logging Level Advanced Setting value. #> [DscProperty()] [LoggingLevel] $LoggingLevel = [LoggingLevel]::Unset <# .DESCRIPTION Event Max Age Enabled Advanced Setting value. #> [DscProperty()] [nullable[bool]] $EventMaxAgeEnabled <# .DESCRIPTION Event Max Age Advanced Setting value. #> [DscProperty()] [nullable[int]] $EventMaxAge <# .DESCRIPTION Task Max Age Enabled Advanced Setting value. #> [DscProperty()] [nullable[bool]] $TaskMaxAgeEnabled <# .DESCRIPTION Task Max Age Advanced Setting value. #> [DscProperty()] [nullable[int]] $TaskMaxAge <# .DESCRIPTION Motd Advanced Setting value. #> [DscProperty()] [string] $Motd <# .DESCRIPTION Indicates whether the Motd content should be cleared. #> [DscProperty()] [bool] $MotdClear <# .DESCRIPTION Issue Advanced Setting value. #> [DscProperty()] [string] $Issue <# .DESCRIPTION Indicates whether the Issue content should be cleared. #> [DscProperty()] [bool] $IssueClear hidden [string] $LogLevelSettingName = "log.level" hidden [string] $EventMaxAgeEnabledSettingName = "event.maxAgeEnabled" hidden [string] $EventMaxAgeSettingName = "event.maxAge" hidden [string] $TaskMaxAgeEnabledSettingName = "task.maxAgeEnabled" hidden [string] $TaskMaxAgeSettingName = "task.maxAge" hidden [string] $MotdSettingName = "etc.motd" hidden [string] $IssueSettingName = "etc.issue" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.UpdatevCenterSettings($this.Connection) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = !$this.ShouldUpdatevCenterSettings() $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [vCenterSettings] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [vCenterSettings]::new() $result.Server = $this.Server $this.PopulateResult($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the advanced settings of the specified vCenter Server. #> [PSObject] GetvCenterAdvancedSettings() { $getAdvancedSettingParams = @{ Server = $this.Connection Entity = $this.Connection ErrorAction = 'Stop' Verbose = $false } return Get-AdvancedSetting @getAdvancedSettingParams } <# .DESCRIPTION Returns a boolean value indicating if at least one Advanced Setting value should be updated. #> [bool] ShouldUpdatevCenterSettings() { $vCenterCurrentAdvancedSettings = $this.GetvCenterAdvancedSettings() $currentLogLevel = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.LogLevelSettingName } $currentEventMaxAgeEnabled = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.EventMaxAgeEnabledSettingName } $currentEventMaxAge = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.EventMaxAgeSettingName } $currentTaskMaxAgeEnabled = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.TaskMaxAgeEnabledSettingName } $currentTaskMaxAge = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.TaskMaxAgeSettingName } $currentMotd = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.MotdSettingName } $currentIssue = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.IssueSettingName } $motdDesiredValue = if ($this.MotdClear) { [string]::Empty } else { $this.Motd } $issueDesiredValue = if ($this.IssueClear) { [string]::Empty } else { $this.Issue } $shouldUpdatevCenterSettings = @( $this.ShouldUpdateDscResourceSetting('LoggingLevel', [string] $currentLogLevel.Value, $this.LoggingLevel.ToString()), $this.ShouldUpdateDscResourceSetting('EventMaxAgeEnabled', $currentEventMaxAgeEnabled.Value, $this.EventMaxAgeEnabled), $this.ShouldUpdateDscResourceSetting('EventMaxAge', $currentEventMaxAge.Value, $this.EventMaxAge), $this.ShouldUpdateDscResourceSetting('TaskMaxAgeEnabled', $currentTaskMaxAgeEnabled.Value, $this.TaskMaxAgeEnabled), $this.ShouldUpdateDscResourceSetting('TaskMaxAge', $currentTaskMaxAge.Value, $this.TaskMaxAge), $this.ShouldUpdateDscResourceSetting('Motd', $currentMotd.Value, $motdDesiredValue), $this.ShouldUpdateDscResourceSetting('Issue', $currentIssue.Value, $issueDesiredValue) ) return ($shouldUpdatevCenterSettings -Contains $true) } <# .DESCRIPTION Sets the desired value for the Advanced Setting, if update of the Advanced Setting value is needed. #> [void] SetAdvancedSetting($advancedSettingName, $advancedSetting, $advancedSettingDesiredValue, $advancedSettingCurrentValue) { if ($this.ShouldUpdateDscResourceSetting($advancedSettingName, $advancedSettingCurrentValue, $advancedSettingDesiredValue)) { $setAdvancedSettingParams = @{ AdvancedSetting = $advancedSetting Value = $advancedSettingDesiredValue Confirm = $false ErrorAction = 'Stop' Verbose = $false } Set-AdvancedSetting @setAdvancedSettingParams } } <# .DESCRIPTION Sets the desired value for the Advanced Setting, if update of the Advanced Setting value is needed. This handles Advanced Settings that have a "Clear" property. #> [void] SetAdvancedSetting($advancedSettingName, $advancedSetting, $advancedSettingDesiredValue, $advancedSettingCurrentValue, $clearValue) { $this.WriteLogUtil('Verbose', "{0} Entering {1}" , @((Get-Date), (Get-PSCallStack)[0].FunctionName)) if ($clearValue) { if ($this.ShouldUpdateDscResourceSetting($advancedSettingName, $advancedSettingCurrentValue, [string]::Empty)) { $setAdvancedSettingParams = @{ AdvancedSetting = $advancedSetting Value = [string]::Empty Confirm = $false ErrorAction = 'Stop' Verbose = $false } Set-AdvancedSetting @setAdvancedSettingParams } } else { $this.SetAdvancedSetting($advancedSettingName, $advancedSetting, $advancedSettingDesiredValue, $advancedSettingCurrentValue) } } <# .DESCRIPTION Performs update on those Advanced Settings values that needs to be updated. #> [void] UpdatevCenterSettings($vCenter) { $vCenterCurrentAdvancedSettings = $this.GetvCenterAdvancedSettings() $currentLogLevel = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.LogLevelSettingName } $currentEventMaxAgeEnabled = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.EventMaxAgeEnabledSettingName } $currentEventMaxAge = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.EventMaxAgeSettingName } $currentTaskMaxAgeEnabled = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.TaskMaxAgeEnabledSettingName } $currentTaskMaxAge = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.TaskMaxAgeSettingName } $currentMotd = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.MotdSettingName } $currentIssue = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.IssueSettingName } $this.SetAdvancedSetting('LoggingLevel', $currentLogLevel, $this.LoggingLevel.ToString(), $currentLogLevel.Value) $this.SetAdvancedSetting('EventMaxAgeEnabled', $currentEventMaxAgeEnabled, $this.EventMaxAgeEnabled, $currentEventMaxAgeEnabled.Value) $this.SetAdvancedSetting('EventMaxAge', $currentEventMaxAge, $this.EventMaxAge, $currentEventMaxAge.Value) $this.SetAdvancedSetting('TaskMaxAgeEnabled', $currentTaskMaxAgeEnabled, $this.TaskMaxAgeEnabled, $currentTaskMaxAgeEnabled.Value) $this.SetAdvancedSetting('TaskMaxAge', $currentTaskMaxAge, $this.TaskMaxAge, $currentTaskMaxAge.Value) $this.SetAdvancedSetting('Motd', $currentMotd, $this.Motd, $currentMotd.Value, $this.MotdClear) $this.SetAdvancedSetting('Issue', $currentIssue, $this.Issue, $currentIssue.Value, $this.IssueClear) } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the advanced settings from the server. #> [void] PopulateResult($result) { $vCenterCurrentAdvancedSettings = $this.GetvCenterAdvancedSettings() $currentLogLevel = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.LogLevelSettingName } $currentEventMaxAgeEnabled = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.EventMaxAgeEnabledSettingName } $currentEventMaxAge = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.EventMaxAgeSettingName } $currentTaskMaxAgeEnabled = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.TaskMaxAgeEnabledSettingName } $currentTaskMaxAge = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.TaskMaxAgeSettingName } $currentMotd = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.MotdSettingName } $currentIssue = $vCenterCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.IssueSettingName } $result.LoggingLevel = $currentLogLevel.Value $result.EventMaxAgeEnabled = $currentEventMaxAgeEnabled.Value $result.EventMaxAge = $currentEventMaxAge.Value $result.TaskMaxAgeEnabled = $currentTaskMaxAgeEnabled.Value $result.TaskMaxAge = $currentTaskMaxAge.Value $result.Motd = $currentMotd.Value $result.Issue = $currentIssue.Value } } [DscResource()] class vCenterStatistics : BaseDSC { <# .DESCRIPTION The unit of period. Statistics can be stored separatelly for each of the {Day, Week, Month, Year} period units. #> [DscProperty(Key)] [Period] $Period <# .DESCRIPTION Period for which the statistics are saved. #> [DscProperty()] [nullable[long]] $PeriodLength <# .DESCRIPTION Specified Level value for the vCenter Statistics. #> [DscProperty()] [nullable[int]] $Level <# .DESCRIPTION If collecting statistics for the specified period unit is enabled. #> [DscProperty()] [nullable[bool]] $Enabled <# .DESCRIPTION Interval in Minutes, indicating the period for collecting statistics. #> [DscProperty()] [nullable[long]] $IntervalMinutes hidden [int] $SecondsInAMinute = 60 [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $performanceManager = $this.GetPerformanceManager() $currentPerformanceInterval = $this.GetPerformanceInterval($performanceManager) $this.UpdatePerformanceInterval($performanceManager, $currentPerformanceInterval) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $performanceManager = $this.GetPerformanceManager() $currentPerformanceInterval = $this.GetPerformanceInterval($performanceManager) $result = !(( $this.ShouldUpdateDscResourceSetting('Level', $currentPerformanceInterval.Level, $this.Level), $this.ShouldUpdateDscResourceSetting('Enabled', $currentPerformanceInterval.Enabled, $this.Enabled), $this.ShouldUpdateDscResourceSetting('IntervalMinutes', $currentPerformanceInterval.SamplingPeriod / $this.SecondsInAMinute, $this.IntervalMinutes), $this.ShouldUpdateDscResourceSetting('PeriodLength', $currentPerformanceInterval.Length / $this.Period, $this.PeriodLength) ) -Contains $true) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [vCenterStatistics] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [vCenterStatistics]::new() $result.Server = $this.Server $result.Period = $this.Period $performanceManager = $this.GetPerformanceManager() $currentPerformanceInterval = $this.GetPerformanceInterval($performanceManager) $result.Level = $currentPerformanceInterval.Level $result.Enabled = $currentPerformanceInterval.Enabled # Converts the Sampling Period from seconds to minutes $result.IntervalMinutes = $currentPerformanceInterval.SamplingPeriod / $this.SecondsInAMinute # Converts the PeriodLength from seconds to the specified Period type $result.PeriodLength = $currentPerformanceInterval.Length / $this.Period return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Returns the Performance Manager for the specified vCenter. #> [PSObject] GetPerformanceManager() { $getViewParams = @{ Server = $this.Connection Id = $this.Connection.ExtensionData.Content.PerfManager ErrorAction = 'Stop' Verbose = $false } return Get-View @getViewParams } <# .DESCRIPTION Returns the Performance Interval for which the new statistics settings should be applied. #> [PSObject] GetPerformanceInterval($performanceManager) { $currentPerformanceInterval = $performanceManager.HistoricalInterval | Where-Object { $_.Name -Match $this.Period } return $currentPerformanceInterval } <# .DESCRIPTION Returns the value to set for the Performance Interval Setting. #> [PSObject] SpecifiedOrCurrentValue($desiredValue, $currentValue) { if ($null -eq $desiredValue) { # Desired value is not specified return $currentValue } else { return $desiredValue } } <# .DESCRIPTION Updates the Performance Interval with the specified settings for vCenter Statistics. #> [void] UpdatePerformanceInterval($performanceManager, $currentPerformanceInterval) { $performanceIntervalArgs = @{ Key = $currentPerformanceInterval.Key Name = $currentPerformanceInterval.Name Enabled = $this.SpecifiedOrCurrentValue($this.Enabled, $currentPerformanceInterval.Enabled) Level = $this.SpecifiedOrCurrentValue($this.Level, $currentPerformanceInterval.Level) SamplingPeriod = $this.SpecifiedOrCurrentValue($this.IntervalMinutes * $this.SecondsInAMinute, $currentPerformanceInterval.SamplingPeriod) Length = $this.SpecifiedOrCurrentValue($this.PeriodLength * $this.Period, $currentPerformanceInterval.Length) } $desiredPerformanceInterval = New-PerformanceInterval @performanceIntervalArgs try { Update-PerfInterval -PerformanceManager $performanceManager -PerformanceInterval $desiredPerformanceInterval } catch { throw "Server operation failed with the following error: $($_.Exception.Message)" } } } [DscResource()] class vCenterVMHost : DatacenterInventoryBaseDSC { vCenterVMHost() { $this.InventoryItemFolderType = [FolderType]::Host } <# .DESCRIPTION Credentials needed for authenticating with the VMHost. #> [DscProperty(Mandatory)] [PSCredential] $VMHostCredential <# .DESCRIPTION Specifies the port on the VMHost used for the connection. #> [DscProperty()] [nullable[int]] $Port <# .DESCRIPTION Indicates whether the VMHost is added to the vCenter if the authenticity of the VMHost SSL certificate cannot be verified. #> [DscProperty()] [nullable[bool]] $Force <# .DESCRIPTION Specifies the location of the Resource Pool in the Cluster. Location consists of 0 or more Resource Pools. The Root Resource Pool of the Cluster is not part of the location. If '/' location is passed, the Resource Pool is the Root Resource Pool of the Cluster. Resource Pools names in the location are separated by '/'. The VMHost's Root Resource Pool becomes the last Resource Pool specified in the location and the VMHost Resource Pool hierarchy is imported into the new nested Resource Pool. Example location for a Resource Pool: 'MyResourcePoolOne/MyResourcePoolTwo'. #> [DscProperty()] [string] $ResourcePoolLocation hidden [string] $ClusterType = 'VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster' hidden [string] $AddVMHostTovCenterMessage = "Adding VMHost {0} to vCenter {1} and location {2}." hidden [string] $MoveVMHostToDestinationMessage = "Moving VMHost {0} to location {1} on vCenter {2}." hidden [string] $RemoveVMHostFromvCenterMessage = "Removing VMHost {0} from vCenter {1}." hidden [string] $FoundLocationIsNotAClusterMessage = "Resource Pool location {0} is specified but the found location {1} for VMHost {2} is not a Cluster." hidden [string] $CouldNotRetrieveRootResourcePoolOfClusterMessage = "Could not retrieve Root Resource Pool of Cluster {0}. For more information: {1}" hidden [string] $InvalidResourcePoolLocationInClusterMessage = "Resource Pool location {0} is not valid in Cluster {2}." hidden [string] $CouldNotAddVMHostTovCenterMessage = "Could not add VMHost {0} to vCenter {1} and location {2}. For more information: {3}" hidden [string] $CouldNotMoveVMHostToDestinationMessage = "Could not move VMHost {0} to location {1} on vCenter {2}. For more information: {3}" hidden [string] $CouldNotRemoveVMHostFromvCenterMessage = "Could not remove VMHost {0} from vCenter {1}. For more information: {2}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $vmHost = $this.GetVMHost() if ($this.Ensure -eq [Ensure]::Present) { $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $vmHostLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) if (![string]::IsNullOrEmpty($this.ResourcePoolLocation)) { <# If the Resource Pool location is specified it means that the desired VMHost location should be a Cluster and the Resource Pool needs to be passed to the cmdlets as VMHost location instead of the Cluster. #> if (!$this.IsVIObjectOfTheCorrectType($vmHostLocation, $this.ClusterType)) { throw ($this.FoundLocationIsNotAClusterMessage -f $this.ResourcePoolLocation, $vmHostLocation.Name, $this.Name) } $rootResourcePool = $this.GetRootResourcePoolOfCluster($vmHostLocation) $vmHostLocation = $this.GetClusterResourcePool($rootResourcePool, $vmHostLocation.Name) } if ($null -eq $vmHost) { $this.AddVMHost($vmHostLocation) } else { if ($vmHost.ParentId -ne $vmHostLocation.Id) { $this.MoveVMHost($vmHost, $vmHostLocation) } } } else { if ($null -ne $vmHost) { $this.RemoveVMHost($vmHost) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $vmHost = $this.GetVMHost() $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $vmHost) { $result = $false } else { $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $vmHostLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $result = ($vmHost.ParentId -eq $vmHostLocation.Id) } } else { $result = ($null -eq $vmHost) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [vCenterVMHost] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [vCenterVMHost]::new() $this.EnsureConnectionIsvCenter() $vmHost = $this.GetVMHost() $this.PopulateResult($result, $vmHost) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the VMHost with the specified name if it is on the vCenter Server system. #> [PSObject] GetVMHost() { return Get-VMHost -Server $this.Connection -Name $this.Name -ErrorAction SilentlyContinue -Verbose:$false } <# .DESCRIPTION Retrieves the Root Resource Pool of the specified Cluster. #> [PSObject] GetRootResourcePoolOfCluster($cluster) { try { $rootResourcePool = Get-ResourcePool -Server $this.Connection -Location $cluster -ErrorAction Stop -Verbose:$false | Where-Object -FilterScript { $_.ParentId -eq $cluster.Id } return $rootResourcePool } catch { throw ($this.CouldNotRetrieveRootResourcePoolOfClusterMessage -f $cluster.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the Resource Pool with the specified name from the specified Cluster. #> [PSObject] GetClusterResourcePool($rootResourcePool, $clusterName) { $clusterResourcePool = $null if ($this.ResourcePoolLocation -eq '/') { # Special case where the Resource Pool location does not contain any Resource Pools. So the Root Resource Pool is the searched Resource Pool from the Cluster. $clusterResourcePool = $rootResourcePool } elseif ($this.ResourcePoolLocation -NotMatch '/') { # Special case where the Resource Pool location is just one Resource Pool. $clusterResourcePool = Get-Inventory -Server $this.Connection -Name $this.ResourcePoolLocation -Location $rootResourcePool -ErrorAction SilentlyContinue -Verbose:$false | Where-Object -FilterScript { $_.ParentId -eq $rootResourcePool.Id } } else { $resourcePoolLocationItems = $this.ResourcePoolLocation -Split '/' # Reverse the Resource Pool location items so that we can start from the bottom and go to the Root Resource Pool. [array]::Reverse($resourcePoolLocationItems) $resourcePoolName = $resourcePoolLocationItems[0] $foundResourcePools = Get-Inventory -Server $this.Connection -Name $resourcePoolName -Location $rootResourcePool -ErrorAction SilentlyContinue -Verbose:$false # Remove the name of the Resource Pool from the Resource Pool location items array as we already retrieved it. $resourcePoolLocationItems = $resourcePoolLocationItems[1..($resourcePoolLocationItems.Length - 1)] <# For every found Resource Pool in the Cluster with the specified name we start to go up through the parents to check if the Resource Pool location is valid. If one of the Parents does not meet the criteria of the Resource Pool location, we continue with the next found Resource Pool. If we find a valid Resource Pool location we stop iterating through the Resource Pools and mark it as the searched Resource Pool from the Cluster. #> foreach ($foundResourcePool in $foundResourcePools) { $foundResourcePoolAsViewObject = Get-View -Server $this.Connection -Id $foundResourcePool.Id -Property Parent -Verbose:$false $validResourcePoolLocation = $true foreach ($resourcePoolLocationItem in $resourcePoolLocationItems) { $foundResourcePoolAsViewObject = Get-View -Server $this.Connection -Id $foundResourcePoolAsViewObject.Parent -Property Name, Parent -Verbose:$false if ($foundResourcePoolAsViewObject.Name -ne $resourcePoolLocationItem) { $validResourcePoolLocation = $false break } } if ($validResourcePoolLocation) { $clusterResourcePool = $foundResourcePool break } } } if ($null -eq $clusterResourcePool) { throw ($this.InvalidResourcePoolLocationInClusterMessage -f $this.ResourcePoolLocation, $clusterName) } return $clusterResourcePool } <# .DESCRIPTION Adds the VMHost to the specified location and to be managed by the vCenter Server system. #> [void] AddVMHost($vmHostLocation) { $addVMHostParams = @{ Server = $this.Connection Name = $this.Name Location = $vmHostLocation Credential = $this.VMHostCredential Confirm = $false ErrorAction = 'Stop' Verbose = $false } if ($null -ne $this.Port) { $addVMHostParams.Port = $this.Port } if ($null -ne $this.Force) { $addVMHostParams.Force = $this.Force } try { $this.WriteLogUtil('Verbose', $this.AddVMHostTovCenterMessage, @($this.Name, $this.Connection.Name, $vmHostLocation.Name)) Add-VMHost @addVMHostParams } catch { throw ($this.CouldNotAddVMHostTovCenterMessage -f $this.Name, $this.Connection.Name, $vmHostLocation.Name, $_.Exception.Message) } } <# .DESCRIPTION Moves the VMHost to the specified location on the vCenter Server system. #> [void] MoveVMHost($vmHost, $vmHostLocation) { $moveVMHostParams = @{ Server = $this.Connection VMHost = $vmHost Destination = $vmHostLocation Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.MoveVMHostToDestinationMessage, @($vmHost.Name, $vmHostLocation.Name, $this.Connection.Name)) Move-VMHost @moveVMHostParams } catch { throw ($this.CouldNotMoveVMHostToDestinationMessage -f $vmHost.Name, $vmHostLocation.Name, $this.Connection.Name, $_.Exception.Message) } } <# .DESCRIPTION Removes the VMHost from the vCenter Server system. #> [void] RemoveVMHost($vmHost) { $removeVMHostParams = @{ Server = $this.Connection VMHost = $vmHost Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.RemoveVMHostFromvCenterMessage, @($vmHost.Name, $this.Connection.Name)) Remove-VMHost @removeVMHostParams } catch { throw ($this.CouldNotRemoveVMHostFromvCenterMessage -f $vmHost.Name, $this.Connection.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHost) { $result.Server = $this.Connection.Name $result.Location = $this.Location $result.DatacenterName = $this.DatacenterName $result.DatacenterLocation = $this.DatacenterLocation $result.Force = $this.Force $result.ResourcePoolLocation = $this.ResourcePoolLocation if ($null -ne $vmHost) { $result.Name = $vmHost.Name $result.Ensure = [Ensure]::Present $result.Port = $vmHost.ExtensionData.Summary.Config.Port } else { $result.Name = $this.Name $result.Ensure = [Ensure]::Absent $result.Port = $this.Port } } } [DscResource()] class VDPortGroup : BaseDSC { <# .DESCRIPTION Specifies the name of the Distributed Port Group. #> [DscProperty(Key)] [string] $Name <# .DESCRIPTION Specifies the name of the vSphere Distributed Switch associated with the Distributed Port Group. #> [DscProperty(Mandatory)] [string] $VdsName <# .DESCRIPTION Value indicating if the Distributed Port Group should be Present or Absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION Specifies a description for the Distributed Port Group. #> [DscProperty()] [string] $Notes <# .DESCRIPTION Specifies the number of ports that the Distributed Port Group will have. If the parameter is not specified, the number of ports for the Distributed Port Group is 128. #> [DscProperty()] [nullable[int]] $NumPorts <# .DESCRIPTION Specifies the port binding setting for the Distributed Port Group. Valid values are Static, Dynamic, and Ephemeral. #> [DscProperty()] [PortBinding] $PortBinding = [PortBinding]::Unset <# .DESCRIPTION Specifies the VLAN ID for the Distributed Port Group. Valid values are integers in the range of 1 to 4094. If 0 is specified, the VLAN type is 'None'. #> [DscProperty()] [nullable[int]] $VLanId <# .DESCRIPTION Specifies the name for the reference Distributed Port Group. The properties of the new Distributed Port Group will be cloned from the reference Distributed Port Group. #> [DscProperty()] [string] $ReferenceVDPortGroupName hidden [string] $RetrieveVDSwitchMessage = "Retrieving distributed switch {0}." hidden [string] $CreateVDPortGroupMessage = "Creating distributed port group {0} on distributed switch {1}." hidden [string] $ModifyVDPortGroupMessage = "Modifying distributed port group {0}." hidden [string] $ModifyVDPortGroupVlanConfigurationMessage = "Modifying the VLAN ID of distributed port group {0} to {1}." hidden [string] $RemoveVDPortGroupMessage = "Removing distributed port group {0} from distributed switch {1}." hidden [string] $CouldNotRetrieveVDSwitchMessage = "Could not retrieve distributed switch {0}. For more information: {1}" hidden [string] $CouldNotCreateVDPortGroupMessage = "Could not create distributed port group {0} on distributed switch {1}. For more information: {2}" hidden [string] $CouldNotModifyVDPortGroupMessage = "Could not modify distributed port group {0}. For more information: {1}" hidden [string] $CouldNotModifyVDPortGroupVlanConfigurationMessage = "Could not modify the VLAN ID of distributed port group {0} to {1}. For more information: {2}" hidden [string] $CouldNotRemoveVDPortGroupMessage = "Could not remove distributed port group {0} from distributed switch {1}. For more information: {2}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $distributedSwitch = $this.GetDistributedSwitch() $distributedPortGroup = $this.GetDistributedPortGroup($distributedSwitch) if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $distributedPortGroup) { $this.AddDistributedPortGroup($distributedSwitch) } else { if ($this.ShouldUpdateVLanId($distributedPortGroup)) { $this.UpdateDistributedPortGroupVlanConfiguration($distributedPortGroup) } if ($this.ShouldUpdateDistributedPortGroup($distributedPortGroup)) { $this.UpdateDistributedPortGroup($distributedPortGroup) } } } else { if ($null -ne $distributedPortGroup) { $this.RemoveDistributedPortGroup($distributedPortGroup) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $distributedSwitch = $this.GetDistributedSwitch() $distributedPortGroup = $this.GetDistributedPortGroup($distributedSwitch) $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $distributedPortGroup) { $result = $false } else { $result = if ($this.ShouldUpdateDistributedPortGroup($distributedPortGroup) -or $this.ShouldUpdateVLanId($distributedPortGroup)) { $false } else { $true } } } else { $result = ($null -eq $distributedPortGroup) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VDPortGroup] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $result = [VDPortGroup]::new() $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $distributedSwitch = $this.GetDistributedSwitch() $distributedPortGroup = $this.GetDistributedPortGroup($distributedSwitch) $this.PopulateResult($distributedPortGroup, $result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the Distributed Switch with the specified name from the server if it exists. If the Distributed Switch does not exist and Ensure is set to 'Absent', $null is returned. Otherwise it throws an exception. #> [PSObject] GetDistributedSwitch() { <# The Verbose logic here is needed to suppress the Verbose output of the Import-Module cmdlet when importing the 'VMware.VimAutomation.Vds' Module. #> $savedVerbosePreference = $global:VerbosePreference $global:VerbosePreference = 'SilentlyContinue' try { $getVDSwitchParams = @{ Server = $this.Connection Name = $this.VdsName Verbose = $false } if ($this.Ensure -eq [Ensure]::Absent) { $getVDSwitchParams.ErrorAction = 'SilentlyContinue' return Get-VDSwitch @getVDSwitchParams } else { try { $this.WriteLogUtil('Verbose', $this.RetrieveVDSwitchMessage, @($this.VdsName)) $getVDSwitchParams.ErrorAction = 'Stop' return Get-VDSwitch @getVDSwitchParams } catch { throw ($this.CouldNotRetrieveVDSwitchMessage -f $this.VdsName, $_.Exception.Message) } } } finally { $global:VerbosePreference = $savedVerbosePreference } } <# .DESCRIPTION Retrieves the Distributed Port Group with the specified name, available on the specified Distributed Switch from the server if it exists. Otherwise returns $null. #> [PSObject] GetDistributedPortGroup($distributedSwitch) { if ($null -eq $distributedSwitch) { <# If the Distributed Switch is $null, it means that Ensure was set to 'Absent' and the Distributed Port Group does not exist for the specified Distributed Switch. #> return $null } $getVDPortgroupParams = @{ Server = $this.Connection Name = $this.Name VDSwitch = $distributedSwitch ErrorAction = 'SilentlyContinue' Verbose = $false } return Get-VDPortgroup @getVDPortgroupParams } <# .DESCRIPTION Checks if the passed Distributed Port Group needs be modified based on the passed properties. #> [bool] ShouldUpdateDistributedPortGroup($distributedPortGroup) { $shouldUpdateDistributedPortGroup = @( $this.ShouldUpdateDscResourceSetting('NumPorts', $distributedPortGroup.NumPorts, $this.NumPorts), $this.ShouldUpdateDscResourceSetting('PortBinding', [string] $distributedPortGroup.PortBinding, $this.PortBinding.ToString()), $this.ShouldUpdateDscResourceSetting('Notes', [string] $distributedPortGroup.Notes, $this.Notes) ) return ($shouldUpdateDistributedPortGroup -Contains $true) } <# .DESCRIPTION Checks if the VLAN ID of the specified distributed port group should be modified. #> [bool] ShouldUpdateVLanId($distributedPortGroup) { $currentVLanId = if ($null -ne $distributedPortGroup.VlanConfiguration) { $distributedPortGroup.VlanConfiguration.VlanId } else { 0 } return $this.ShouldUpdateDscResourceSetting('VLanId', $currentVLanId, $this.VLanId) } <# .DESCRIPTION Returns the populated Distributed Port Group parameters. #> [hashtable] GetDistributedPortGroupParams() { $distributedPortGroupParams = @{} $distributedPortGroupParams.Server = $this.Connection $distributedPortGroupParams.Confirm = $false $distributedPortGroupParams.ErrorAction = 'Stop' $distributedPortGroupParams.Verbose = $false if ($null -ne $this.Notes) { $distributedPortGroupParams.Notes = $this.Notes } if ($null -ne $this.NumPorts) { $distributedPortGroupParams.NumPorts = $this.NumPorts } if ($this.PortBinding -ne [PortBinding]::Unset) { $distributedPortGroupParams.PortBinding = $this.PortBinding.ToString() } return $distributedPortGroupParams } <# .DESCRIPTION Creates a new Distributed Port Group available on the specified Distributed Switch. #> [void] AddDistributedPortGroup($distributedSwitch) { $distributedPortGroupParams = $this.GetDistributedPortGroupParams() $distributedPortGroupParams.Name = $this.Name $distributedPortGroupParams.VDSwitch = $distributedSwitch <# ReferencePortGroup and VLanId are parameters only for the New-VDPortgroup cmdlet and are not used for the Set-VDPortgroup cmdlet. #> if (![string]::IsNullOrEmpty($this.ReferenceVDPortGroupName)) { $distributedPortGroupParams.ReferencePortgroup = $this.ReferenceVDPortGroupName } if ($null -ne $this.VLanId) { $distributedPortGroupParams.VlanId = $this.VLanId } try { $this.WriteLogUtil('Verbose', $this.CreateVDPortGroupMessage, @($this.Name, $distributedSwitch.Name)) New-VDPortgroup @distributedPortGroupParams } catch { throw ($this.CouldNotCreateVDPortGroupMessage -f $this.Name, $distributedSwitch.Name, $_.Exception.Message) } } <# .DESCRIPTION Modifies the configuration of the specified Distributed Port Group with the passed properties. #> [void] UpdateDistributedPortGroup($distributedPortGroup) { $distributedPortGroupParams = $this.GetDistributedPortGroupParams() try { $this.WriteLogUtil('Verbose', $this.ModifyVDPortGroupMessage, @($distributedPortGroup.Name)) $distributedPortGroup | Set-VDPortgroup @distributedPortGroupParams } catch { throw ($this.CouldNotModifyVDPortGroupMessage -f $distributedPortGroup.Name, $_.Exception.Message) } } <# .DESCRIPTION Modifies the VLAN configuration of the specified Distributed Port Group. #> [void] UpdateDistributedPortGroupVlanConfiguration($distributedPortGroup) { $setVDVlanConfigurationParams = @{ VDPortgroup = $distributedPortGroup Confirm = $false ErrorAction = 'Stop' Verbose = $false } if ($this.VLanId -eq 0) { $setVDVlanConfigurationParams.DisableVlan = $true } else { $setVDVlanConfigurationParams.VlanId = $this.VLanId } try { $this.WriteLogUtil('Verbose', $this.ModifyVDPortGroupVlanConfigurationMessage, @($distributedPortGroup.Name, $this.VLanId)) Set-VDVlanConfiguration @setVDVlanConfigurationParams } catch { throw ($this.CouldNotModifyVDPortGroupVlanConfigurationMessage -f $distributedPortGroup.Name, $this.VLanId, $_.Exception.Message) } } <# .DESCRIPTION Removes the specified Distributed Port Group from the vSphere Distributed Switch that it belongs to. #> [void] RemoveDistributedPortGroup($distributedPortGroup) { try { $this.WriteLogUtil('Verbose', $this.RemoveVDPortGroupMessage, @($distributedPortGroup.Name, $distributedPortGroup.VDSwitch.Name)) $removeVDPortGroupParams = @{ Server = $this.Connection VDPortGroup = $distributedPortGroup Confirm = $false ErrorAction = 'Stop' Verbose = $false } Remove-VDPortGroup @removeVDPortGroupParams } catch { throw ($this.CouldNotRemoveVDPortGroupMessage -f $distributedPortGroup.Name, $distributedPortGroup.VDSwitch.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get() method. #> [void] PopulateResult($distributedPortGroup, $result) { $result.Server = $this.Connection.Name $result.ReferenceVDPortGroupName = $this.ReferenceVDPortGroupName if ($null -ne $distributedPortGroup) { $result.Name = $distributedPortGroup.Name $result.VdsName = $distributedPortGroup.VDSwitch.Name $result.Ensure = [Ensure]::Present $result.Notes = $distributedPortGroup.Notes $result.NumPorts = $distributedPortGroup.NumPorts $result.PortBinding = $distributedPortGroup.PortBinding.ToString() $result.VLanId = [int] $distributedPortGroup.VlanConfiguration.VlanId } else { $result.Name = $this.Name $result.VdsName = $this.VdsName $result.Ensure = [Ensure]::Absent $result.Notes = $this.Notes $result.NumPorts = $this.NumPorts $result.PortBinding = $this.PortBinding $result.VLanId = [int] $this.VLanId } } } [DscResource()] class VDSwitch : DatacenterInventoryBaseDSC { DistributedSwitch() { $this.InventoryItemFolderType = [FolderType]::Network } <# .DESCRIPTION Specifies the contact details of the vSphere Distributed Switch administrator. #> [DscProperty()] [string] $ContactDetails <# .DESCRIPTION Specifies the name of the vSphere Distributed Switch administrator. #> [DscProperty()] [string] $ContactName <# .DESCRIPTION Specifies the discovery protocol type of the vSphere Distributed Switch that you want to create. The valid values are CDP, LLDP and Unset. If you do not set a value for this parameter, the default server setting is used. #> [DscProperty()] [LinkDiscoveryProtocolProtocol] $LinkDiscoveryProtocol = [LinkDiscoveryProtocolProtocol]::Unset <# .DESCRIPTION Specifies the link discovery protocol operation for the vSphere Distributed Switch that you want to create. The valid values are Advertise, Both, Listen, None and Unset. If you do not set a value for this parameter, the default server setting is used. #> [DscProperty()] [LinkDiscoveryProtocolOperation] $LinkDiscoveryProtocolOperation = [LinkDiscoveryProtocolOperation]::Unset <# .DESCRIPTION Specifies the maximum number of ports allowed on the vSphere Distributed Switch that you want to create. #> [DscProperty()] [nullable[int]] $MaxPorts <# .DESCRIPTION Specifies the maximum MTU size for the vSphere Distributed Switch that you want to create. Valid values are positive integers only. #> [DscProperty()] [nullable[int]] $Mtu <# .DESCRIPTION Specifies a description for the vSphere Distributed Switch that you want to create. #> [DscProperty()] [string] $Notes <# .DESCRIPTION Specifies the number of uplink ports on the vSphere Distributed Switch that you want to create. #> [DscProperty()] [nullable[int]] $NumUplinkPorts <# .DESCRIPTION Specifies the Name for the reference vSphere Distributed Switch. The properties of the new vSphere Distributed Switch will be cloned from the reference vSphere Distributed Switch. #> [DscProperty()] [string] $ReferenceVDSwitchName <# .DESCRIPTION Specifies the version of the vSphere Distributed Switch that you want to create. You cannot specify a version that is incompatible with the version of the vCenter Server system you are connected to. #> [DscProperty()] [string] $Version <# .DESCRIPTION Indicates whether the new vSphere Distributed Switch will be created without importing the port groups from the specified reference vSphere Distributed Switch. #> [DscProperty()] [nullable[bool]] $WithoutPortGroups [void] Set() { try { $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $distributedSwitchLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $distributedSwitch = $this.GetDistributedSwitch($distributedSwitchLocation) if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $distributedSwitch) { $this.AddDistributedSwitch($distributedSwitchLocation) } else { $this.UpdateDistributedSwitch($distributedSwitch) } } else { if ($null -ne $distributedSwitch) { $this.RemoveDistributedSwitch($distributedSwitch) } } } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $distributedSwitchLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $distributedSwitch = $this.GetDistributedSwitch($distributedSwitchLocation) $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $distributedSwitch) { $result = $false } else { $result = !$this.ShouldUpdateDistributedSwitch($distributedSwitch) } } else { $result = ($null -eq $distributedSwitch) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VDSwitch] Get() { try { $result = [VDSwitch]::new() $result.Server = $this.Server $result.Location = $this.Location $result.DatacenterName = $this.DatacenterName $result.DatacenterLocation = $this.DatacenterLocation $result.ReferenceVDSwitchName = $this.ReferenceVDSwitchName $result.WithoutPortGroups = $this.WithoutPortGroups $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $distributedSwitchLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $distributedSwitch = $this.GetDistributedSwitch($distributedSwitchLocation) $this.PopulateResult($distributedSwitch, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the Distributed Switch from the specified Location in the Datacenter if it exists, otherwise returns $null. #> [PSObject] GetDistributedSwitch($distributedSwitchLocation) { <# The Verbose logic here is needed to suppress the Exporting and Importing of the cmdlets from the VMware.VimAutomation.Vds Module. #> $savedVerbosePreference = $global:VerbosePreference $global:VerbosePreference = 'SilentlyContinue' $distributedSwitch = Get-VDSwitch -Server $this.Connection -Name $this.Name -Location $distributedSwitchLocation -ErrorAction SilentlyContinue $global:VerbosePreference = $savedVerbosePreference return $distributedSwitch } <# .DESCRIPTION Checks if the passed Distributed Switch needs be updated based on the specified properties. #> [bool] ShouldUpdateDistributedSwitch($distributedSwitch) { $shouldUpdateDistributedSwitch = @( $this.ShouldUpdateDscResourceSetting('ContactDetails', [string] $distributedSwitch.ContactDetails, $this.ContactDetails), $this.ShouldUpdateDscResourceSetting('ContactName', [string] $distributedSwitch.ContactName, $this.ContactName), $this.ShouldUpdateDscResourceSetting('Notes', [string] $distributedSwitch.Notes, $this.Notes), $this.ShouldUpdateDscResourceSetting('Version', [string] $distributedSwitch.Version, $this.Version), $this.ShouldUpdateDscResourceSetting('MaxPorts', $distributedSwitch.MaxPorts, $this.MaxPorts), $this.ShouldUpdateDscResourceSetting('Mtu', $distributedSwitch.Mtu, $this.Mtu), $this.ShouldUpdateDscResourceSetting('NumUplinkPorts', $distributedSwitch.NumUplinkPorts, $this.NumUplinkPorts), $this.ShouldUpdateDscResourceSetting( 'LinkDiscoveryProtocol', [string] $distributedSwitch.LinkDiscoveryProtocol, $this.LinkDiscoveryProtocol.ToString() ), $this.ShouldUpdateDscResourceSetting( 'LinkDiscoveryProtocolOperation', [string] $distributedSwitch.LinkDiscoveryProtocolOperation, $this.LinkDiscoveryProtocolOperation.ToString() ) ) return ($shouldUpdateDistributedSwitch -Contains $true) } <# .DESCRIPTION Returns the populated Distributed Switch parameters. #> [hashtable] GetDistributedSwitchParams() { $distributedSwitchParams = @{} $distributedSwitchParams.Server = $this.Connection $distributedSwitchParams.Confirm = $false $distributedSwitchParams.ErrorAction = 'Stop' if (![string]::IsNullOrEmpty($this.ContactDetails)) { $distributedSwitchParams.ContactDetails = $this.ContactDetails } if (![string]::IsNullOrEmpty($this.ContactName)) { $distributedSwitchParams.ContactName = $this.ContactName } if (![string]::IsNullOrEmpty($this.Notes)) { $distributedSwitchParams.Notes = $this.Notes } if (![string]::IsNullOrEmpty($this.Version)) { $distributedSwitchParams.Version = $this.Version } if ($null -ne $this.MaxPorts) { $distributedSwitchParams.MaxPorts = $this.MaxPorts } if ($null -ne $this.Mtu) { $distributedSwitchParams.Mtu = $this.Mtu } if ($null -ne $this.NumUplinkPorts) { $distributedSwitchParams.NumUplinkPorts = $this.NumUplinkPorts } if ($this.LinkDiscoveryProtocol -ne [LinkDiscoveryProtocolProtocol]::Unset) { $distributedSwitchParams.LinkDiscoveryProtocol = $this.LinkDiscoveryProtocol.ToString() } if ($this.LinkDiscoveryProtocolOperation -ne [LinkDiscoveryProtocolOperation]::Unset) { $distributedSwitchParams.LinkDiscoveryProtocolOperation = $this.LinkDiscoveryProtocolOperation.ToString() } return $distributedSwitchParams } <# .DESCRIPTION Creates a new Distributed Switch with the specified properties at the specified location. #> [void] AddDistributedSwitch($distributedSwitchLocation) { $distributedSwitchParams = $this.GetDistributedSwitchParams() $distributedSwitchParams.Name = $this.Name $distributedSwitchParams.Location = $distributedSwitchLocation <# ReferenceVDSwitch and WithoutPortGroups are parameters only for the New-VDSwitch cmdlet and are not used for the Set-VDSwitch cmdlet. #> if (![string]::IsNullOrEmpty($this.ReferenceVDSwitchName)) { $distributedSwitchParams.ReferenceVDSwitch = $this.ReferenceVDSwitchName } if ($null -ne $this.WithoutPortGroups) { $distributedSwitchParams.WithoutPortGroups = $this.WithoutPortGroups } try { New-VDSwitch @distributedSwitchParams } catch { throw "Cannot create Distributed Switch $($this.Name) at Location $($distributedSwitchLocation.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Updates the Distributed Switch with the specified properties. #> [void] UpdateDistributedSwitch($distributedSwitch) { $distributedSwitchParams = $this.GetDistributedSwitchParams() try { $distributedSwitch | Set-VDSwitch @distributedSwitchParams } catch { throw "Cannot update Distributed Switch $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Removes the Distributed Switch from the specified location. #> [void] RemoveDistributedSwitch($distributedSwitch) { try { $distributedSwitch | Remove-VDSwitch -Server $this.Connection -Confirm:$false -ErrorAction Stop } catch { throw "Cannot remove Distributed Switch $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Distributed Switch from the server. #> [void] PopulateResult($distributedSwitch, $result) { if ($null -ne $distributedSwitch) { $result.Name = $distributedSwitch.Name $result.Ensure = [Ensure]::Present $result.ContactDetails = $distributedSwitch.ContactDetails $result.ContactName = $distributedSwitch.ContactName $result.LinkDiscoveryProtocol = $distributedSwitch.LinkDiscoveryProtocol.ToString() $result.LinkDiscoveryProtocolOperation = $distributedSwitch.LinkDiscoveryProtocolOperation.ToString() $result.MaxPorts = $distributedSwitch.MaxPorts $result.Mtu = $distributedSwitch.Mtu $result.Notes = $distributedSwitch.Notes $result.NumUplinkPorts = $distributedSwitch.NumUplinkPorts $result.Version = $distributedSwitch.Version } else { $result.Name = $this.Name $result.Ensure = [Ensure]::Absent $result.ContactDetails = $this.ContactDetails $result.ContactName = $this.ContactName $result.LinkDiscoveryProtocol = $this.LinkDiscoveryProtocol $result.LinkDiscoveryProtocolOperation = $this.LinkDiscoveryProtocolOperation $result.MaxPorts = $this.MaxPorts $result.Mtu = $this.Mtu $result.Notes = $this.Notes $result.NumUplinkPorts = $this.NumUplinkPorts $result.Version = $this.Version } } } [DscResource()] class VDSwitchVMHost : BaseDSC { <# .DESCRIPTION Specifies the name of the vSphere Distributed Switch to/from which you want to add/remove the specified VMHosts. #> [DscProperty(Key)] [string] $VdsName <# .DESCRIPTION Specifies the names of the VMHosts that you want to add/remove to/from the specified vSphere Distributed Switch. #> [DscProperty(Mandatory)] [string[]] $VMHostNames <# .DESCRIPTION Value indicating if the VMHosts should be Present/Absent to/from the specified vSphere Distributed Switch. #> [DscProperty(Mandatory)] [Ensure] $Ensure [void] Set() { try { $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $distributedSwitch = $this.GetDistributedSwitch() $vmHosts = $this.GetVMHosts() $filteredVMHosts = $this.GetFilteredVMHosts($vmHosts, $distributedSwitch) if ($this.Ensure -eq [Ensure]::Present) { $this.AddVMHostsToDistributedSwitch($filteredVMHosts, $distributedSwitch) } else { $this.RemoveVMHostsFromDistributedSwitch($filteredVMHosts, $distributedSwitch) } } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $distributedSwitch = $this.GetDistributedSwitch() $vmHosts = $this.GetVMHosts() if ($null -eq $distributedSwitch) { # If the Distributed Switch is $null, it means that Ensure is 'Absent' and the Distributed Switch does not exist. return $true } return !$this.ShouldUpdateVMHostsInDistributedSwitch($vmHosts, $distributedSwitch) } finally { $this.DisconnectVIServer() } } [VDSwitchVMHost] Get() { try { $result = [VDSwitchVMHost]::new() $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $distributedSwitch = $this.GetDistributedSwitch() $vmHosts = $this.GetVMHosts() $result.Server = $this.Connection.Name $result.VMHostNames = $vmHosts | Select-Object -ExpandProperty Name $result.Ensure = $this.Ensure if ($null -eq $distributedSwitch) { # If the Distributed Switch is $null, it means that Ensure is 'Absent' and the Distributed Switch does not exist. $result.VdsName = $this.Name } else { $result.VdsName = $distributedSwitch.Name } return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the Distributed Switch with the specified name from the server if it exists. If the Distributed Switch does not exist and Ensure is set to 'Absent', $null is returned. Otherwise it throws an exception. #> [PSObject] GetDistributedSwitch() { if ($this.Ensure -eq [Ensure]::Absent) { $distributedSwitch = Get-VDSwitch -Server $this.Connection -Name $this.VdsName -ErrorAction SilentlyContinue return $distributedSwitch } else { try { $distributedSwitch = Get-VDSwitch -Server $this.Connection -Name $this.VdsName -ErrorAction Stop return $distributedSwitch } catch { throw "Could not retrieve Distributed Switch $($this.VdsName). For more information: $($_.Exception.Message)" } } } <# .DESCRIPTION Retrieves the VMHosts with the specified names from the server if they exist. For every VMHost that does not exist, a warning message is shown to the user without throwing an exception. #> [array] GetVMHosts() { $vmHosts = @() foreach ($vmHostName in $this.VMHostNames) { $vmHost = Get-VMHost -Server $this.Connection -Name $vmHostName -ErrorAction SilentlyContinue if ($null -eq $vmHost) { $this.WriteLogUtil('Warning', "The passed VMHost {0} was not found and it will be ignored.", @($vmHostName)) } else { $vmHosts += $vmHost } } return $vmHosts } <# .DESCRIPTION Checks if VMHosts should be added/removed from the Distributed Switch depending on the value of the Ensure property. If Ensure is set to 'Present', checks if all passed VMHosts are part of the Distributed Switch. If Ensure is set to 'Absent', checks if all passed VMHosts are not part of the Distributed Switch. #> [bool] ShouldUpdateVMHostsInDistributedSwitch($vmHosts, $distributedSwitch) { if ($this.Ensure -eq [Ensure]::Present) { foreach ($vmHost in $vmHosts) { $addedVMHost = $vmHost.ExtensionData.Config.Network.ProxySwitch | Where-Object -FilterScript { $_.DvsName -eq $distributedSwitch.Name } if ($null -eq $addedVMHost) { return $true } } } else { foreach ($vmHost in $vmHosts) { $removedVMHost = $vmHost.ExtensionData.Config.Network.ProxySwitch | Where-Object -FilterScript { $_.DvsName -eq $distributedSwitch.Name } if ($null -ne $removedVMHost) { return $true } } } return $false } <# .DESCRIPTION Returns the filtered VMHosts based on the value of the Ensure property. If Ensure is set to 'Present', it returns only these VMHosts that are currently not part of the Distributed Switch. The other VMHosts in the array are ignored because they are already part of the Distributed Switch. If Ensure is set to 'Absent', it returns only these VMHosts that are currently part of the Distributed Switch. The other VMHosts in the array are ignored because they are not part of the Distributed Switch. In both cases a warning message is shown to the user when a specific VMHost is ignored. #> [array] GetFilteredVMHosts($vmHosts, $distributedSwitch) { $filteredVMHosts = @() if ($this.Ensure -eq [Ensure]::Present) { foreach ($vmHost in $vmHosts) { $addedVMHost = $vmHost.ExtensionData.Config.Network.ProxySwitch | Where-Object -FilterScript { $_.DvsName -eq $distributedSwitch.Name } if ($null -ne $addedVMHost) { $this.WriteLogUtil('Warning', "VMHost {0} is already added to Distributed Switch {1} and it will be ignored.", @($vmHost.Name, $distributedSwitch.Name)) continue } $filteredVMHosts += $vmHost } } else { foreach ($vmHost in $vmHosts) { $removedVMHost = $vmHost.ExtensionData.Config.Network.ProxySwitch | Where-Object -FilterScript { $_.DvsName -eq $distributedSwitch.Name } if ($null -eq $removedVMHost) { $this.WriteLogUtil('Warning', "VMHost {0} is not added to Distributed Switch {1} and it will be ignored.", @($vmHost.Name, $distributedSwitch.Name)) continue } $filteredVMHosts += $vmHost } } return $filteredVMHosts } <# .DESCRIPTION Adds the VMHosts to the specified Distributed Switch. #> [void] AddVMHostsToDistributedSwitch($filteredVMHosts, $distributedSwitch) { try { Add-VDSwitchVMHost -Server $this.Connection -VDSwitch $distributedSwitch -VMHost $filteredVMHosts -ErrorAction Stop } catch { throw "Could not add VMHosts $filteredVMHosts to Distributed Switch $($distributedSwitch.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Removes the VMHosts from the specified Distributed Switch. #> [void] RemoveVMHostsFromDistributedSwitch($filteredVMHosts, $distributedSwitch) { try { Remove-VDSwitchVMHost -Server $this.Connection -VDSwitch $distributedSwitch -VMHost $filteredVMHosts -ErrorAction Stop } catch { throw "Could not remove VMHosts $filteredVMHosts from Distributed Switch $($distributedSwitch.Name). For more information: $($_.Exception.Message)" } } } [DscResource()] class VMHostAccount : BaseDSC { <# .DESCRIPTION Specifies the ID for the host account. #> [DscProperty(Key)] [string] $Id <# .DESCRIPTION Value indicating if the Resource should be Present or Absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION Permission on the VMHost entity is created for the specified User Id with the specified Role. #> [DscProperty(Mandatory)] [string] $Role <# .DESCRIPTION Specifies the Password for the host account. #> [DscProperty()] [string] $AccountPassword <# .DESCRIPTION Provides a description for the host account. The maximum length of the text is 255 symbols. #> [DscProperty()] [string] $Description hidden [string] $AccountPasswordParameterName = 'Password' hidden [string] $DescriptionParameterName = 'Description' [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsESXi() $vmHostAccount = $this.GetVMHostAccount() if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $vmHostAccount) { $this.AddVMHostAccount() } else { $this.UpdateVMHostAccount($vmHostAccount) } } else { if ($null -ne $vmHostAccount) { $this.RemoveVMHostAccount($vmHostAccount) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsESXi() $vmHostAccount = $this.GetVMHostAccount() $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $vmHostAccount) { $result = $false } else { $result = if ($this.ShouldUpdateVMHostAccount($vmHostAccount) -or $this.ShouldCreateAcountPermission($vmHostAccount)) { $false } else { $true } } } else { $result = ($null -eq $vmHostAccount) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostAccount] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostAccount]::new() $result.Server = $this.Server $this.EnsureConnectionIsESXi() $vmHostAccount = $this.GetVMHostAccount() $this.PopulateResult($vmHostAccount, $result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Returns the VMHost Account if it exists, otherwise returns $null. #> [PSObject] GetVMHostAccount() { $getVMHostAccountParams = @{ Server = $this.Connection Id = $this.Id ErrorAction = 'SilentlyContinue' Verbose = $false } return Get-VMHostAccount @getVMHostAccountParams } <# .DESCRIPTION Checks if a new Permission with the passed Role needs to be created for the specified VMHost Account. #> [bool] ShouldCreateAcountPermission($vmHostAccount) { $getVIPermissionParams = @{ Server = $this.Connection Entity = $this.Server Principal = $vmHostAccount ErrorAction = 'SilentlyContinue' Verbose = $false } $existingPermission = Get-VIPermission @getVIPermissionParams return ($null -eq $existingPermission) } <# .DESCRIPTION Checks if the VMHost Account should be updated. #> [bool] ShouldUpdateVMHostAccount($vmHostAccount) { <# If the Account Password is passed, we should check if we can connect to the ESXi host with the passed Id and Password. If we can connect to the host it means that the password is in the desired state so we should close the connection and continue checking the other passed properties. If we cannot connect to the host it means that the desired Password is not equal to the current Password of the Account. #> if ($null -ne $this.AccountPassword) { $connectVIServerParams = @{ Server = $this.Server User = $this.Id Password = $this.AccountPassword ErrorAction = 'SilentlyContinue' Verbose = $false } $hostConnection = Connect-VIServer @connectVIServerParams if ($null -eq $hostConnection) { return $true } else { $disconnectVIServerParams = @{ Server = $hostConnection ErrorAction = 'SilentlyContinue' Verbose = $false Confirm = $false } Disconnect-VIServer @disconnectVIServerParams } } return $this.ShouldUpdateDscResourceSetting('Description', $vmHostAccount.Description, $this.Description) } <# .DESCRIPTION Populates the parameters for the New-VMHostAccount and Set-VMHostAccount cmdlets. #> [void] PopulateVMHostAccountParams($vmHostAccountParams, $parameter, $desiredValue) { if ($null -ne $desiredValue) { $vmHostAccountParams.$parameter = $desiredValue } } <# .DESCRIPTION Returns the populated VMHost Account parameters. #> [hashtable] GetVMHostAccountParams() { $vmHostAccountParams = @{} $vmHostAccountParams.Server = $this.Connection $vmHostAccountParams.Confirm = $false $vmHostAccountParams.ErrorAction = 'Stop' $vmHostAccountParams.Verbose = $false $this.PopulateVMHostAccountParams($vmHostAccountParams, $this.AccountPasswordParameterName, $this.AccountPassword) $this.PopulateVMHostAccountParams($vmHostAccountParams, $this.DescriptionParameterName, $this.Description) return $vmHostAccountParams } <# .DESCRIPTION Creates a new Permission with the passed Role for the specified VMHost Account. #> [void] CreateAccountPermission($vmHostAccount) { $getVIRoleParams = @{ Server = $this.Connection Name = $this.Role ErrorAction = 'SilentlyContinue' Verbose = $false } $accountRole = Get-VIRole @getVIRoleParams if ($null -eq $accountRole) { throw "The passed role $($this.Role) is not present on the server." } try { $newVIPermissionParams = @{ Server = $this.Connection Entity = $this.Server Principal = $vmHostAccount Role = $accountRole ErrorAction = 'Stop' Verbose = $false } New-VIPermission @newVIPermissionParams } catch { throw "Cannot assign role $($this.Role) to account $($vmHostAccount.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Creates a new VMHost Account with the specified properties. #> [void] AddVMHostAccount() { $vmHostAccountParams = $this.GetVMHostAccountParams() $vmHostAccountParams.Id = $this.Id $vmHostAccount = $null try { $vmHostAccount = New-VMHostAccount @vmHostAccountParams } catch { throw "Cannot create VMHost Account $($this.Id). For more information: $($_.Exception.Message)" } $this.CreateAccountPermission($vmHostAccount) } <# .DESCRIPTION Updates the VMHost Account with the specified properties. #> [void] UpdateVMHostAccount($vmHostAccount) { $vmHostAccountParams = $this.GetVMHostAccountParams() try { $vmHostAccount | Set-VMHostAccount @vmHostAccountParams } catch { throw "Cannot update VMHost Account $($this.Id). For more information: $($_.Exception.Message)" } if ($this.ShouldCreateAcountPermission($vmHostAccount)) { $this.CreateAccountPermission($vmHostAccount) } } <# .DESCRIPTION Removes the VMHost Account. #> [void] RemoveVMHostAccount($vmHostAccount) { try { $removeVMHostAccountParams = @{ Server = $this.Connection HostAccount = $vmHostAccount ErrorAction = 'Stop' Verbose = $false Confirm = $false } Remove-VMHostAccount @removeVMHostAccountParams } catch { throw "Cannot remove VMHost Account $($this.Id). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the VMHost Account from the server. #> [void] PopulateResult($vmHostAccount, $result) { if ($null -ne $vmHostAccount) { $getVIPermissionParams = @{ Server = $this.Connection Entity = $this.Server Principal = $vmHostAccount ErrorAction = 'SilentlyContinue' Verbose = $false } $permission = Get-VIPermission @getVIPermissionParams $result.Id = $vmHostAccount.Id $result.Ensure = [Ensure]::Present $result.Role = $permission.Role $result.Description = $vmHostAccount.Description } else { $result.Id = $this.Id $result.Ensure = [Ensure]::Absent $result.Role = $this.Role $result.Description = $this.Description } } } [DscResource()] class VMHostAdvancedSettings : VMHostBaseDSC { <# .DESCRIPTION Hashtable containing the advanced settings of the specified VMHost, where each key-value pair represents one advanced setting - the key is the name of the setting and the value is the desired value for the setting. #> [DscProperty(Mandatory)] [hashtable] $AdvancedSettings [void] Set() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.UpdateVMHostAdvancedSettings($vmHost) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() return !$this.ShouldUpdateVMHostAdvancedSettings($vmHost) } finally { $this.DisconnectVIServer() } } [VMHostAdvancedSettings] Get() { try { $result = [VMHostAdvancedSettings]::new() $result.Server = $this.Server $this.ConnectVIServer() $vmHost = $this.GetVMHost() $result.Name = $vmHost.Name $result.AdvancedSettings = @{} $this.PopulateResult($vmHost, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the Advanced Settings of the specified VMHost from the server. #> [array] GetAdvancedSettings($vmHost) { try { $retrievedAdvancedSettings = Get-AdvancedSetting -Server $this.Connection -Entity $vmHost -ErrorAction Stop return $retrievedAdvancedSettings } catch { throw "Could not retrieve the Advanced Settings of VMHost $($vmHost.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Returns the Advanced Setting if it is present in the retrieved Advanced Settings from the server. Otherwise returns $null. #> [PSObject] GetAdvancedSetting($retrievedAdvancedSettings, $advancedSettingName, $vmHostName) { $advancedSetting = $retrievedAdvancedSettings | Where-Object { $_.Name -eq $advancedSettingName } if ($null -eq $advancedSetting) { <# Here a warning log is used instead of 'throw' to ensure that the execution will not stop if an invalid Advanced Setting is present in the passed hashtable and in the same time to provide an information to the user that invalid data is passed. #> $this.WriteLogUtil('Warning', "Advanced Setting {0} does not exist for VMHost {1} and will be ignored.", @($advancedSettingName, $vmHostName)) } return $advancedSetting } <# .DESCRIPTION Converts the desired value of the Advanced Setting from the hashtable to the correct type of the value from the server. Works only for primitive types. #> [object] ConvertAdvancedSettingDesiredValueToCorrectType($advancedSetting) { $advancedSettingDesiredValue = $null if ($advancedSetting.Value -is [bool]) { # For bool values the '-as' operator returns 'True' for both 'true' and 'false' strings so specific conversion is needed. $advancedSettingDesiredValue = [System.Convert]::ToBoolean($this.AdvancedSettings[$advancedSetting.Name]) } else { $advancedSettingDesiredValue = $this.AdvancedSettings[$advancedSetting.Name] -as $advancedSetting.Value.GetType() } return $advancedSettingDesiredValue } <# .DESCRIPTION Checks if the Advanced Setting should be updated depending on the passed desired value. #> [bool] ShouldUpdateVMHostAdvancedSetting($advancedSetting) { <# Each element in the hashtable is of type MSFT_KeyValuePair where the value is a string. So before comparison the value needs to be converted to its original type which can be retrieved from its server value. #> $advancedSettingDesiredValue = $this.ConvertAdvancedSettingDesiredValueToCorrectType($advancedSetting) $result = $advancedSettingDesiredValue -ne $advancedSetting.Value if ($result) { $this.WriteLogUtil('Verbose', $this.SettingIsNotInDesiredStateMessage, @( $advancedSetting.Name, $advancedSetting.Value, $advancedSettingDesiredValue )) } return $result } <# .DESCRIPTION Checks if any of the Advanced Settings present in the hashtable need to be updated. #> [bool] ShouldUpdateVMHostAdvancedSettings($vmHost) { $retrievedAdvancedSettings = $this.GetAdvancedSettings($vmHost) foreach ($advancedSettingName in $this.AdvancedSettings.Keys) { $advancedSetting = $this.GetAdvancedSetting($retrievedAdvancedSettings, $advancedSettingName, $vmHost.Name) if ($null -ne $advancedSetting -and $this.ShouldUpdateVMHostAdvancedSetting($advancedSetting)) { return $true } } return $false } <# .DESCRIPTION Returns the Advanced Option Manager of the specified VMHost. #> [PSObject] GetVMHostAdvancedOptionManager($vmHost) { try { $vmHostAdvancedOptionManager = Get-View -Server $this.Connection -Id $vmHost.ExtensionData.ConfigManager.AdvancedOption -ErrorAction Stop return $vmHostAdvancedOptionManager } catch { throw "VMHost Advanced Option Manager could not be retrieved. For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Performs an update on these Advanced Settings that are present in the hashtable and need to be updated. #> [void] UpdateVMHostAdvancedSettings($vmHost) { $retrievedAdvancedSettings = $this.GetAdvancedSettings($vmHost) $vmHostAdvancedOptionManager = $this.GetVMHostAdvancedOptionManager($vmHost) $options = @() foreach ($advancedSettingName in $this.AdvancedSettings.Keys) { $advancedSetting = $this.GetAdvancedSetting($retrievedAdvancedSettings, $advancedSettingName, $vmHost.Name) if ($null -ne $advancedSetting -and $this.ShouldUpdateVMHostAdvancedSetting($advancedSetting)) { <# Each element in the hashtable is of type MSFT_KeyValuePair where the value is a string. So before setting the value of the option, we need to convert it to its original type which can be retrieved from its server value. #> $option = New-Object VMware.Vim.OptionValue $option.Key = $advancedSettingName $option.Value = $this.ConvertAdvancedSettingDesiredValueToCorrectType($advancedSetting) $options += $option } } if ($options.Length -eq 0) { return } try { Update-VMHostAdvancedSettings -VMHostAdvancedOptionManager $vmHostAdvancedOptionManager -Options $options } catch { throw "The Advanced Settings Update operation failed with the following error: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Advanced Settings from the server. #> [void] PopulateResult($vmHost, $result) { $retrievedAdvancedSettings = $this.GetAdvancedSettings($vmHost) foreach ($advancedSettingName in $this.AdvancedSettings.Keys) { $advancedSetting = $this.GetAdvancedSetting($retrievedAdvancedSettings, $advancedSettingName, $vmHost.Name) if ($null -ne $advancedSetting) { <# The LCM converts the hashtable to MSFT_KeyValuePair class which has the following properties: Key of type string and Value of type string. So the value of the Advanced Setting from the server should be converted to string to avoid an error to be thrown. This will only work for primitive types like bool, int, long and so on. If a non-primitive type is introduced for Advanced Setting, invalid result will be returned from the conversion. #> $result.AdvancedSettings[$advancedSettingName] = $advancedSetting.Value.ToString() } } } } [DscResource()] class VMHostAgentVM : VMHostBaseDSC { <# .DESCRIPTION Specifies the Datastore used for deploying Agent VMs on this host. #> [DscProperty()] [string] $AgentVmDatastore <# .DESCRIPTION Specifies the Management Network for Agent VMs on this host. #> [DscProperty()] [string] $AgentVmNetwork hidden [string] $AgentVmDatastoreName = 'AgentVmDatastore' hidden [string] $AgentVmNetworkName = 'AgentVmNetwork' hidden [string] $GetAgentVmDatastoreAsViewObjectMethodName = 'GetAgentVmDatastoreAsViewObject' hidden [string] $GetAgentVmNetworkAsViewObjectMethodName = 'GetAgentVmNetworkAsViewObject' [void] Set() { try { $this.ConnectVIServer() # vCenter Connection is needed to retrieve the EsxAgentHostManager. $this.EnsureConnectionIsvCenter() $vmHost = $this.GetVMHost() $esxAgentHostManager = $this.GetEsxAgentHostManager($vmHost) $this.UpdateAgentVMConfiguration($vmHost, $esxAgentHostManager) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() # vCenter Connection is needed to retrieve the EsxAgentHostManager. $this.EnsureConnectionIsvCenter() $vmHost = $this.GetVMHost() $esxAgentHostManager = $this.GetEsxAgentHostManager($vmHost) return !$this.ShouldUpdateAgentVMConfiguration($esxAgentHostManager) } finally { $this.DisconnectVIServer() } } [VMHostAgentVM] Get() { try { $result = [VMHostAgentVM]::new() $result.Server = $this.Server $this.ConnectVIServer() # vCenter Connection is needed to retrieve the EsxAgentHostManager. $this.EnsureConnectionIsvCenter() $vmHost = $this.GetVMHost() $esxAgentHostManager = $this.GetEsxAgentHostManager($vmHost) $result.Name = $vmHost.Name $this.PopulateResult($esxAgentHostManager, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the EsxAgentHostManager of the specified VMHost from the server. #> [PSObject] GetEsxAgentHostManager($vmHost) { try { $esxAgentHostManager = Get-View -Server $this.Connection -Id $vmHost.ExtensionData.ConfigManager.EsxAgentHostManager -ErrorAction Stop return $esxAgentHostManager } catch { throw "Could not retrieve the EsxAgentHostManager of VMHost $($vmHost.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Retrieves the AgentVM Datastore of the EsxAgentHostManager of the specified VMHost from the server as a View Object. #> [PSObject] GetAgentVmDatastoreAsViewObject($esxAgentHostManager) { try { $datastoreAsViewObject = Get-View -Server $this.Connection -Id $esxAgentHostManager.ConfigInfo.AgentVmDatastore -ErrorAction Stop return $datastoreAsViewObject } catch { throw "Could not retrieve the AgentVM Datastore of the EsxAgentHostManager. For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Retrieves the AgentVM Network of the EsxAgentHostManager of the specified VMHost from the server as a View Object. #> [PSObject] GetAgentVmNetworkAsViewObject($esxAgentHostManager) { try { $networkAsViewObject = Get-View -Server $this.Connection -Id $esxAgentHostManager.ConfigInfo.AgentVmNetwork -ErrorAction Stop return $networkAsViewObject } catch { throw "Could not retrieve the AgentVM Network of the EsxAgentHostManager. For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Checks if the AgentVM Setting needs to be updated with the desired value. #> [bool] ShouldUpdateAgentVMSetting($esxAgentHostManager, $agentVmSetting, $agentVmSettingName, $getAgentVmSettingAsViewObjectMethodName) { if ($null -eq $agentVmSetting) { if ($null -ne $esxAgentHostManager.ConfigInfo.$agentVmSettingName) { return $true } } else { if ($null -eq $esxAgentHostManager.ConfigInfo.$agentVmSettingName) { return $true } else { $agentVmSettingAsViewObject = $this.$getAgentVmSettingAsViewObjectMethodName($esxAgentHostManager) if ($agentVmSetting -ne $agentVmSettingAsViewObject.Name) { $this.WriteLogUtil('Verbose', $this.SettingIsNotInDesiredStateMessage, @( $agentVmSettingName, $agentVmSettingAsViewObject.Name, $agentVmSetting )) return $true } } } return $false } <# .DESCRIPTION Checks if the AgentVM Configuration needs to be updated with the desired values. #> [bool] ShouldUpdateAgentVMConfiguration($esxAgentHostManager) { if ($this.ShouldUpdateAgentVMSetting($esxAgentHostManager, $this.AgentVmDatastore, $this.AgentVmDatastoreName, $this.GetAgentVmDatastoreAsViewObjectMethodName)) { return $true } elseif ($this.ShouldUpdateAgentVMSetting($esxAgentHostManager, $this.AgentVmNetwork, $this.AgentVmNetworkName, $this.GetAgentVmNetworkAsViewObjectMethodName)) { return $true } else { return $false } } <# .DESCRIPTION Retrieves the Datastore for AgentVM from the server if it exists. If the Datastore name is not passed it returns $null and if the Datastore does not exist it throws an exception. #> [PSObject] GetDatastoreForAgentVM($vmHost) { if ($null -eq $this.AgentVmDatastore) { return $null } try { $datastore = Get-Datastore -Server $this.Connection -Name $this.AgentVmDatastore -RelatedObject $vmHost -ErrorAction Stop return $datastore.ExtensionData.MoRef } catch { throw "Could not retrieve Datastore $($this.AgentVmDatastore) for VMHost $($vmHost.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Retrieves the Network for AgentVM from the specified VMHost if it exists. If the Network name is not passed it returns $null and if the Network does not exist it throws an exception. #> [PSObject] GetNetworkForAgentVM($vmHost) { if ($null -eq $this.AgentVmNetwork) { return $null } $foundNetwork = $null $networks = $vmHost.ExtensionData.Network foreach ($network in $networks) { $networkAsViewObject = Get-View -Server $this.Connection -Id $network if ($this.AgentVmNetwork -eq $networkAsViewObject.Name) { $foundNetwork = $network break } } if ($null -eq $foundNetwork) { throw "Could not find Network $($this.AgentVmNetwork) for VMHost $($vmHost.Name)." } return $foundNetwork } <# .DESCRIPTION Performs an update on the AgentVM Configuration of the specified VMHost by setting the Datastore and Network. #> [void] UpdateAgentVMConfiguration($vmHost, $esxAgentHostManager) { $datastore = $this.GetDatastoreForAgentVM($vmHost) $network = $this.GetNetworkForAgentVM($vmHost) $configInfo = New-Object VMware.Vim.HostEsxAgentHostManagerConfigInfo $configInfo.AgentVmDatastore = $datastore $configInfo.AgentVmNetwork = $network try { Update-AgentVMConfiguration -EsxAgentHostManager $esxAgentHostManager -EsxAgentHostManagerConfigInfo $configInfo } catch { throw "The AgentVM Configuration of VMHost $($vmHost.Name) could not be updated: $($_.Exception.Message)" } } <# .DESCRIPTION Returns the AgentVM Setting value from the Configuration on the server. #> [string] PopulateAgentVmSetting($esxAgentHostManager, $agentVmSettingName, $getAgentVmSettingAsViewObjectMethodName) { if ($null -eq $esxAgentHostManager.ConfigInfo.$agentVmSettingName) { return $null } else { $agentVmSettingAsViewObject = $this.$getAgentVmSettingAsViewObjectMethodName($esxAgentHostManager) return $agentVmSettingAsViewObject.Name } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the AgentVM Settings from the server. #> [void] PopulateResult($esxAgentHostManager, $result) { $result.AgentVmDatastore = $this.PopulateAgentVmSetting($esxAgentHostManager, $this.AgentVmDatastoreName, $this.GetAgentVmDatastoreAsViewObjectMethodName) $result.AgentVmNetwork = $this.PopulateAgentVmSetting($esxAgentHostManager, $this.AgentVmNetworkName, $this.GetAgentVmNetworkAsViewObjectMethodName) } } [DscResource()] class VMHostAuthentication : VMHostBaseDSC { <# .DESCRIPTION Specifies the name of the domain to join/leave. The name should be the fully qualified domain name (FQDN). #> [DscProperty(Mandatory)] [string] $DomainName <# .DESCRIPTION Value indicating if the specified VMHost should join/leave the specified domain. #> [DscProperty(Mandatory)] [DomainAction] $DomainAction <# .DESCRIPTION The credentials needed for joining the specified domain. #> [DscProperty()] [PSCredential] $DomainCredential [void] Set() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostAuthenticationInfo = $this.GetVMHostAuthenticationInfo($vmHost) if ($this.DomainAction -eq [DomainAction]::Join) { $this.JoinDomain($vmHostAuthenticationInfo, $vmHost) } else { $this.LeaveDomain($vmHostAuthenticationInfo, $vmHost) } } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostAuthenticationInfo = $this.GetVMHostAuthenticationInfo($vmHost) $result = $null if ($this.DomainAction -eq [DomainAction]::Join) { $result = !$this.ShouldUpdateDscResourceSetting('DomainName', [string] $vmHostAuthenticationInfo.Domain, $this.DomainName) } else { $result = ($null -eq $vmHostAuthenticationInfo.Domain) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostAuthentication] Get() { try { $result = [VMHostAuthentication]::new() $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostAuthenticationInfo = $this.GetVMHostAuthenticationInfo($vmHost) $this.PopulateResult($vmHostAuthenticationInfo, $vmHost, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the Authentication information for the specified VMHost. #> [PSObject] GetVMHostAuthenticationInfo($vmHost) { try { $vmHostAuthenticationInfo = Get-VMHostAuthentication -Server $this.Connection -VMHost $vmHost -ErrorAction Stop return $vmHostAuthenticationInfo } catch { throw "Could not retrieve Authentication information for VMHost $($vmHost.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Includes the specified VMHost in the specified domain. #> [void] JoinDomain($vmHostAuthenticationInfo, $vmHost) { $setVMHostAuthenticationParams = @{ VMHostAuthentication = $vmHostAuthenticationInfo Domain = $this.DomainName Credential = $this.DomainCredential JoinDomain = $true Confirm = $false ErrorAction = 'Stop' } try { Set-VMHostAuthentication @setVMHostAuthenticationParams } catch { throw "Could not include VMHost $($vmHost.Name) in domain $($this.DomainName). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Excludes the specified VMHost from the specified domain. #> [void] LeaveDomain($vmHostAuthenticationInfo, $vmHost) { $setVMHostAuthenticationParams = @{ VMHostAuthentication = $vmHostAuthenticationInfo LeaveDomain = $true Force = $true Confirm = $false ErrorAction = 'Stop' } try { Set-VMHostAuthentication @setVMHostAuthenticationParams } catch { throw "Could not exclude VMHost $($vmHost.Name) from domain $($this.DomainName). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method. #> [void] PopulateResult($vmHostAuthenticationInfo, $vmHost, $result) { $result.Server = $this.Connection.Name $result.Name = $vmHost.Name if ($null -ne $vmHostAuthenticationInfo.Domain) { $result.DomainName = $vmHostAuthenticationInfo.Domain $result.DomainAction = [DomainAction]::Join } else { $result.DomainName = $this.DomainName $result.DomainAction = [DomainAction]::Leave } } } [DscResource()] class VMHostCache : VMHostBaseDSC { <# .DESCRIPTION Specifies the name of the Datastore used for swap performance enhancement. #> [DscProperty(Key)] [string] $DatastoreName <# .DESCRIPTION Specifies the space to allocate on the specified Datastore to implement swap performance enhancements, in GB. This value should be less than or equal to the free space capacity of the Datastore. #> [DscProperty(Mandatory)] [double] $SwapSizeGB [void] Set() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.UpdateHostCacheConfiguration($vmHost) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() return !$this.ShouldUpdateHostCacheConfiguration($vmHost) } finally { $this.DisconnectVIServer() } } [VMHostCache] Get() { try { $result = [VMHostCache]::new() $result.Server = $this.Server $this.ConnectVIServer() $vmHost = $this.GetVMHost() $result.Name = $vmHost.Name $this.PopulateResult($vmHost, $result) return $result } finally { $this.DisconnectVIServer() } } hidden [int] $NumberOfFractionalDigits = 3 <# .DESCRIPTION Retrieves the Cache Configuration Manager of the specified VMHost from the server. #> [PSObject] GetVMHostCacheConfigurationManager($vmHost) { try { $vmHostCacheConfigurationManager = Get-View -Server $this.Connection -Id $vmHost.ExtensionData.ConfigManager.CacheConfigurationManager -ErrorAction Stop return $vmHostCacheConfigurationManager } catch { throw "Could not retrieve the Cache Configuration Manager of VMHost $($vmHost.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Retrieves the Datastore for Host Cache Configuration from the server if it exists. If the Datastore does not exist, it throws an exception. #> [PSObject] GetDatastore($vmHost) { try { $foundDatastore = Get-Datastore -Server $this.Connection -Name $this.DatastoreName -RelatedObject $vmHost -ErrorAction Stop return $foundDatastore } catch { throw "Could not retrieve Datastore $($this.DatastoreName) for VMHost $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Retrieves the Cache Info for the specified Datastore from the Host Cache Configuration. If the Datastore is not enabled for swap performance, it throws an exception. #> [PSObject] GetDatastoreCacheInfo($vmHostCacheConfigurationManager, $foundDatastore) { $datastoreCacheInfo = $vmHostCacheConfigurationManager.CacheConfigurationInfo | Where-Object { $_.Key -eq $foundDatastore.ExtensionData.MoRef } if ($null -eq $datastoreCacheInfo) { throw "Datastore $($foundDatastore.Name) could not be found in enabled for swap performance Datastores." } return $datastoreCacheInfo } <# .DESCRIPTION Converts the passed MB value to GB value by rounding it down with 3 fractional digits in the return value. #> [double] ConvertMBValueToGBValue($mbValue) { return [Math]::Round($mbValue * 1MB / 1GB, $this.NumberOfFractionalDigits) } <# .DESCRIPTION Converts the passed GB value to MB value by rounding it down. #> [long] ConvertGBValueToMBValue($gbValue) { return [long] [Math]::Round($gbValue * 1GB / 1MB) } <# .DESCRIPTION Checks if the Host Cache Configuration should be updated for the specified VMHost by checking if the current Swap Size is equal to the desired one for the specified Datastore. #> [bool] ShouldUpdateHostCacheConfiguration($vmHost) { $vmHostCacheConfigurationManager = $this.GetVMHostCacheConfigurationManager($vmHost) $foundDatastore = $this.GetDatastore($vmHost) $datastoreCacheInfo = $this.GetDatastoreCacheInfo($vmHostCacheConfigurationManager, $foundDatastore) return $this.ShouldUpdateDscResourceSetting('SwapSizeGB', $this.ConvertMBValueToGBValue($datastoreCacheInfo.SwapSize), $this.SwapSizeGB) } <# .DESCRIPTION Performs an update on the Host Cache Configuration of the specified VMHost by changing the Swap Size for the specified Datastore. #> [void] UpdateHostCacheConfiguration($vmHost) { $vmHostCacheConfigurationManager = $this.GetVMHostCacheConfigurationManager($vmHost) $foundDatastore = $this.GetDatastore($vmHost) if ($this.SwapSizeGB -lt 0) { throw "The passed Swap Size $($this.SwapSizeGB) is less than zero." } if ($this.SwapSizeGB -gt $foundDatastore.FreeSpaceGB) { throw "The passed Swap Size $($this.SwapSizeGB) is larger than the free space of the Datastore $($foundDatastore.Name)." } $hostCacheConfigurationSpec = New-Object VMware.Vim.HostCacheConfigurationSpec $hostCacheConfigurationSpec.Datastore = $foundDatastore.ExtensionData.MoRef $hostCacheConfigurationSpec.SwapSize = $this.ConvertGBValueToMBValue($this.SwapSizeGB) $hostCacheConfigurationResult = Update-HostCacheConfiguration -VMHostCacheConfigurationManager $vmHostCacheConfigurationManager -Spec $hostCacheConfigurationSpec $hostCacheConfigurationTask = Get-Task -Server $this.Connection -Id $hostCacheConfigurationResult try { Wait-Task -Task $hostCacheConfigurationTask -ErrorAction Stop } catch { throw "An error occured while updating Cache Configuration for VMHost $($this.Name). For more information: $($_.Exception.Message)" } $this.WriteLogUtil('Verbose', "Cache Configuration was successfully updated for VMHost {0}.", @($this.Name)) } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Host Cache Configuration from the server. #> [void] PopulateResult($vmHost, $result) { $vmHostCacheConfigurationManager = $this.GetVMHostCacheConfigurationManager($vmHost) $foundDatastore = $this.GetDatastore($vmHost) $datastoreCacheInfo = $this.GetDatastoreCacheInfo($vmHostCacheConfigurationManager, $foundDatastore) $result.DatastoreName = $foundDatastore.Name $result.SwapSizeGB = $this.ConvertMBValueToGBValue($datastoreCacheInfo.SwapSize) } } [DscResource()] class VMHostConfiguration : VMHostBaseDSC { <# .DESCRIPTION Specifies the state of the VMHost. If there are powered on VMs on the VMHost, the VMHost can be set into Maintenance mode, only if it is a part of a Drs-enabled Cluster. Before entering Maintenance mode, if the VMHost is fully automated, all powered on VMs are relocated. If the VMHost is not fully automated, a Drs recommendation is generated and all powered on VMs are relocated or powered off. Valid values are Connected, Disconnected and Maintenance. #> [DscProperty()] [VMHostState] $State = [VMHostState]::Unset <# .DESCRIPTION Specifies the license key to be used by the VMHost. You can set the VMHost to evaluation mode by passing the '00000-00000-00000-00000-00000' evaluation key. #> [DscProperty()] [string] $LicenseKey <# .DESCRIPTION Specifies the name of the Time Zone for the VMHost. #> [DscProperty()] [string] $TimeZoneName <# .DESCRIPTION Specifies the name of the Datastore that is visible to the VMHost and can be used for storing swapfiles for the VMs that run on the VMHost. Using a VMHost-specific swap location might degrade the VMotion performance. #> [DscProperty()] [string] $VMSwapfileDatastoreName <# .DESCRIPTION Specifies the swapfile placement policy. Valid values are InHostDatastore and WithVM. #> [DscProperty()] [VMSwapfilePolicy] $VMSwapfilePolicy = [VMSwapfilePolicy]::Unset <# .DESCRIPTION Specifies the name of the host profile associated with the VMHost. If the value is an empty string, the current profile association should not exist. #> [DscProperty()] [string] $HostProfileName <# .DESCRIPTION Specifies the name of the KmsCluster which is used to generate a key to set the VMHost. If the property is passed and the VMHost is not in CryptoSafe state, the DSC Resource makes the VMHost CryptoSafe. If the property is passed and the VMHost is already in CryptoSafe state, the DSC Resource resets the CryptoKey in the VMHost. #> [DscProperty()] [string] $KmsClusterName <# .DESCRIPTION If the value is $true, vCenter Server system automatically reregisters the VMs that are compatible for reregistration. If they are not compatible, they remain on the VMHost. The Evacuate property is valid only when the connection is to a vCenter Server system and the State property is 'Maintenance'. Also, the VMHost must be in a Drs-enabled Cluster. #> [DscProperty()] [nullable[bool]] $Evacuate <# .DESCRIPTION Specifies the special action to take regarding Virtual SAN data when moving in Maintenance mode. The VsanDataMigrationMode property is valid only when the connection is to a vCenter Server system and the State property is 'Maintenance'. #> [DscProperty()] [VsanDataMigrationMode] $VsanDataMigrationMode = [VsanDataMigrationMode]::Unset hidden [string] $ClusterType = 'VMware.VimAutomation.ViCore.Types.V1.Inventory.Cluster' hidden [int] $EnterMaintenanceModeTaskSecondsToSleep = 5 hidden [string] $ModifyVMHostConfigurationMessage = "Modifying the configuration of VMHost {0}." hidden [string] $ApplyingDrsRecommendationsFromClusterMessage = "Applying Drs Recommendations from Cluster {0}." hidden [string] $VMHostStateWasChangedSuccessfullyMessage = "The state of the VMHost {0} was changed successfully to {1}." hidden [string] $ModifyVMHostCryptoKeyMessage = "Modifying the Crypto Key of VMHost {0} by using Kms Cluster {1}." hidden [string] $CouldNotRetrieveTimeZoneMessage = "Could not retrieve Time Zone {0} available on VMHost {1}. For more information: {2}" hidden [string] $CouldNotRetrieveDatastoreMessage = "Could not retrieve Datastore {0} from VMHost {1}. For more information: {2}" hidden [string] $CouldNotRetrieveHostProfileMessage = "Could not retrieve Host Profile {0} from Server {1}. For more information: {2}" hidden [string] $CouldNotRetrieveHostProfileAssociatedWithVMHostMessage = "Could not retrieve Host Profile associated with VMHost {0}. For more information: {1}" hidden [string] $CouldNotRetrieveKmsClusterMessage = "Could not retrieve Kms Cluster {0} from the Server. For more information: {1}" hidden [string] $CouldNotModifyVMHostConfigurationMessage = "Could not modify the configuration of VMHost {0}. For more information: {1}" hidden [string] $CouldNotModifyVMHostCryptoKeyMessage = "Could not modify the Crypto Key of VMHost {0} by using Kms Cluster {1}. For more information: {2}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() if ($this.ShouldModifyVMHostConfiguration($vmHost)) { $setVMHostParams = $this.GetVMHostParamsToModify($vmHost) if ($this.ShouldApplyDrsRecommendation($vmHost)) { $this.ModifyVMHostConfigurationAndApplyDrsRecommendation($setVMHostParams) } else { $this.ModifyVMHostConfiguration($setVMHostParams) } } if ($this.ShouldModifyCryptoKeyOfVMHost($vmHost)) { $this.ModifyVMHostCryptoKey($vmHost) } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $result = $true if ($this.ShouldModifyVMHostConfiguration($vmHost) -or $this.ShouldModifyCryptoKeyOfVMHost($vmHost)) { $result = $false } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostConfiguration] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostConfiguration]::new() $vmHost = $this.GetVMHost() $this.PopulateResult($result, $vmHost) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the Time Zone with the specified name available on the specified VMHost. #> [PSObject] GetVMHostTimeZone($vmHost) { $getVMHostAvailableTimeZoneParams = @{ Server = $this.Connection Name = $this.TimeZoneName VMHost = $vmHost ErrorAction = 'Stop' Verbose = $false } try { $vmHostTimeZone = Get-VMHostAvailableTimeZone @getVMHostAvailableTimeZoneParams return $vmHostTimeZone } catch { throw ($this.CouldNotRetrieveTimeZoneMessage -f $this.TimeZoneName, $vmHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the Datastore with the specified name that is used for storing swapfiles for the VMs that run on the specified VMHost. #> [PSObject] GetVMSwapfileDatastore($vmHost) { $getDatastoreParams = @{ Server = $this.Connection Name = $this.VMSwapfileDatastoreName VMHost = $vmHost ErrorAction = 'Stop' Verbose = $false } try { $vmSwapfileDatastore = Get-Datastore @getDatastoreParams return $vmSwapfileDatastore } catch { throw ($this.CouldNotRetrieveDatastoreMessage -f $this.VMSwapfileDatastoreName, $vmHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the Host Profile with the specified name from the Server. #> [PSObject] GetHostProfile() { $getVMHostProfileParams = @{ Server = $this.Connection Name = $this.HostProfileName ErrorAction = 'Stop' Verbose = $false } try { $hostProfile = Get-VMHostProfile @getVMHostProfileParams return $hostProfile } catch { throw ($this.CouldNotRetrieveHostProfileMessage -f $this.HostProfileName, $this.Connection.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the Host Profile associated with the specified VMHost if the VMHost is associated with a Host Profile. Otherwise returns $null, which indicates that the VMHost is not associated with a Host Profile. #> [PSObject] GetHostProfileAssociatedWithVMHost($vmHost) { $getVMHostProfileParams = @{ Server = $this.Connection Entity = $vmHost ErrorAction = 'SilentlyContinue' Verbose = $false } if (![string]::IsNullOrEmpty($this.HostProfileName)) { $getVMHostProfileParams.Name = $this.HostProfileName } try { $hostProfileAssociatedWithVMHost = Get-VMHostProfile @getVMHostProfileParams return $hostProfileAssociatedWithVMHost } catch { throw ($this.CouldNotRetrieveHostProfileAssociatedWithVMHostMessage -f $vmHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the Kms Cluster with the specified name from the Server. #> [PSObject] GetKmsCluster() { try { $kmsCluster = Get-KmsCluster -Server $this.Connection -Name $this.KmsClusterName -ErrorAction Stop -Verbose:$false return $kmsCluster } catch { throw ($this.CouldNotRetrieveKmsClusterMessage -f $this.KmsClusterName, $_.Exception.Message) } } <# .DESCRIPTION Populates and returns the parameters which should be modified with the Set-VMHost cmdlet. #> [hashtable] GetVMHostParamsToModify($vmHost) { $setVMHostParams = @{ Server = $this.Connection VMHost = $vmHost Confirm = $false ErrorAction = 'Stop' Verbose = $false } if ( ![string]::IsNullOrEmpty($this.LicenseKey) -and $this.LicenseKey -ne $vmHost.LicenseKey ) { $setVMHostParams.LicenseKey = $this.LicenseKey } if ( ![string]::IsNullOrEmpty($this.TimeZoneName) -and $this.TimeZoneName -ne $vmHost.TimeZone.Name ) { $setVMHostParams.TimeZone = $this.GetVMHostTimeZone($vmHost) } if ( ![string]::IsNullOrEmpty($this.VMSwapfileDatastoreName) -and $this.VMSwapfileDatastoreName -ne $vmHost.VMSwapfileDatastore.Name ) { $setVMHostParams.VMSwapfileDatastore = $this.GetVMSwapfileDatastore($vmHost) } if ( $this.VMSwapfilePolicy -ne [VMSwapfilePolicy]::Unset -and $this.VMSwapfilePolicy.ToString() -ne $vmHost.VMSwapfilePolicy.ToString() ) { $setVMHostParams.VMSwapfilePolicy = $this.VMSwapfilePolicy.ToString() } if ($null -ne $this.HostProfileName) { if ($this.HostProfileName -eq [string]::Empty) { # Profile value '$null' cannot be passed if the specified VMHost does not have a Host Profile associated with it. $hostProfileAssociatedWithVMHost = $this.GetHostProfileAssociatedWithVMHost($vmHost) if ($null -ne $hostProfileAssociatedWithVMHost) { $setVMHostParams.Profile = $null } } else { $setVMHostParams.Profile = $this.GetHostProfile() } } if ( $this.State -ne [VMHostState]::Unset -and $this.State.ToString() -ne $vmHost.ConnectionState.ToString() ) { $setVMHostParams.State = $this.State.ToString() if ($this.State -eq [VMHostState]::Maintenance) { if ($null -ne $this.Evacuate) { $setVMHostParams.Evacuate = $this.Evacuate } if ($this.VsanDataMigrationMode -ne [VsanDataMigrationMode]::Unset) { $setVMHostParams.VsanDataMigrationMode = $this.VsanDataMigrationMode.ToString() } } } return $setVMHostParams } <# .DESCRIPTION Checks if the configuration of the specified VMHost should be modified. #> [bool] ShouldModifyVMHostConfiguration($vmHost) { $shouldModifyVMHostConfiguration = @( $this.ShouldUpdateDscResourceSetting('State', [string] $vmHost.ConnectionState, $this.State.ToString()), $this.ShouldUpdateDscResourceSetting('LicenseKey', [string] $vmHost.LicenseKey, $this.LicenseKey), $this.ShouldUpdateDscResourceSetting('TimeZoneName', [string] $vmHost.TimeZone.Name, $this.TimeZoneName), $this.ShouldUpdateDscResourceSetting('VMSwapfileDatastoreName', [string] $vmHost.VMSwapfileDatastore.Name, $this.VMSwapfileDatastoreName), $this.ShouldUpdateDscResourceSetting('VMSwapfilePolicy', [string] $vmHost.VMSwapfilePolicy, $this.VMSwapfilePolicy.ToString()) ) <# If the Host Profile name is specified and it is an empty string, the VMHost should not be associated with a Host Profile. If the Host Profile name is not an empty string, the VMHost should be associated with the specified Host Profile. #> if ($null -ne $this.HostProfileName) { $hostProfileAssociatedWithVMHost = $this.GetHostProfileAssociatedWithVMHost($vmHost) if ($this.HostProfileName -eq [string]::Empty) { $shouldModifyVMHostConfiguration += ($null -ne $hostProfileAssociatedWithVMHost) } else { $shouldModifyVMHostConfiguration += ($null -eq $hostProfileAssociatedWithVMHost) } } return ($shouldModifyVMHostConfiguration -Contains $true) } <# .DESCRIPTION Checks if the CryptoKey of the specified VMHost should be modified. #> [bool] ShouldModifyCryptoKeyOfVMHost($vmHost) { $result = $false if (![string]::IsNullOrEmpty($this.KmsClusterName)) { $kmsCluster = $this.GetKmsCluster() $result = ($kmsCluster.Id -ne $vmHost.ExtensionData.Runtime.CryptoKeyId.ProviderId.Id) } return $result } <# .DESCRIPTION Checks if a Drs recommendation should be generated and applied. A Drs recommendation is generated when the following criteria is met: 1. State property is specified with 'Maintenance' value. 2. The current State of the VMHost is not 'Maintenance'. 3. The VMHost is part of a Drs Cluster and the Cluster is not 'Fully Automated'. #> [bool] ShouldApplyDrsRecommendation($vmHost) { $result = $false $vmHostParent = $vmHost.Parent if ( $this.State -ne [VMHostState]::Unset -and $this.State -eq [VMHostState]::Maintenance -and $vmHost.ConnectionState.ToString() -ne ([VMHostState]::Maintenance).ToString() -and $this.IsVIObjectOfTheCorrectType($vmHostParent, $this.ClusterType) ) { $clusterAutomationLevel = $vmHostParent.DrsAutomationLevel.ToString() $result = ( $vmHostParent.DrsEnabled -and $clusterAutomationLevel -ne ([DrsAutomationLevel]::FullyAutomated).ToString() ) } return $result } <# .DESCRIPTION Modifies the specified configuration parameters of the VMHost. Generates and applies the Drs Recommendation from the Cluster of the specified VMHost. #> [void] ModifyVMHostConfigurationAndApplyDrsRecommendation($setVMHostParams) { $setVMHostParams.RunAsync = $true try { $this.WriteLogUtil('Verbose', $this.ModifyVMHostConfigurationMessage, @($setVMHostParams.VMHost.Name)) $modifyVMHostConfigurationTask = Set-VMHost @setVMHostParams # The Drs Recommendation is not generated immediately after 'EnterMaintenance' task generation. Start-Sleep -Seconds $this.EnterMaintenanceModeTaskSecondsToSleep $getDrsRecommendationParams = @{ Server = $this.Connection Cluster = $setVMHostParams.VMHost.Parent Refresh = $true ErrorAction = 'Stop' Verbose = $false } $clusterDrsRecommendations = Get-DrsRecommendation @getDrsRecommendationParams if ($null -ne $clusterDrsRecommendations) { $applyDrsRecommendationParams = @{ DrsRecommendation = $clusterDrsRecommendations RunAsync = $true Confirm = $false ErrorAction = 'Stop' Verbose = $false } # All generated Drs Recommendations from the Cluster should be applied and when the Task is completed all powered on VMs are relocated or powered off. $this.WriteLogUtil('Verbose', $this.ApplyingDrsRecommendationsFromClusterMessage, @($setVMHostParams.VMHost.Parent.Name)) $applyDrsRecommendationTask = Apply-DrsRecommendation @applyDrsRecommendationParams Wait-Task -Task $applyDrsRecommendationTask -ErrorAction Stop -Verbose:$false } Wait-Task -Task $modifyVMHostConfigurationTask -ErrorAction Stop -Verbose:$false $vmHost = $this.GetVMHost() # The state of the VMHost should be verified when the Task completes. if ($vmHost.ConnectionState.ToString() -eq ([VMHostState]::Maintenance).ToString()) { $this.WriteLogUtil('Verbose', $this.VMHostStateWasChangedSuccessfullyMessage, @($vmHost.Name, $vmHost.ConnectionState.ToString())) } } catch { throw ($this.CouldNotModifyVMHostConfigurationMessage -f $setVMHostParams.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Modifies the specified configuration parameters of the VMHost. #> [void] ModifyVMHostConfiguration($setVMHostParams) { try { $this.WriteLogUtil('Verbose', $this.ModifyVMHostConfigurationMessage, @($setVMHostParams.VMHost.Name)) Set-VMHost @setVMHostParams } catch { throw ($this.CouldNotModifyVMHostConfigurationMessage -f $setVMHostParams.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Modifies the CryptoKey of the specified VMHost. #> [void] ModifyVMHostCryptoKey($vmHost) { $setVMHostParams = @{ Server = $this.Connection VMHost = $vmHost Confirm = $false ErrorAction = 'Stop' Verbose = $false } $kmsCluster = $this.GetKmsCluster() $setVMHostParams.KmsCluster = $kmsCluster try { $this.WriteLogUtil('Verbose', $this.ModifyVMHostCryptoKeyMessage, @($vmHost.Name, $kmsCluster.Name)) Set-VMHost @setVMHostParams } catch { throw ($this.CouldNotModifyVMHostCryptoKeyMessage -f $vmHost.Name, $kmsCluster.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHost) { $result.Server = $this.Connection.Name $result.Name = $vmHost.Name $result.State = $vmHost.ConnectionState.ToString() $result.Evacuate = $this.Evacuate $result.VsanDataMigrationMode = $this.VsanDataMigrationMode $result.LicenseKey = $vmHost.LicenseKey $result.TimeZoneName = $vmHost.TimeZone.Name $result.VMSwapfileDatastoreName = $vmHost.VMSwapfileDatastore.Name $result.VMSwapfilePolicy = $vmHost.VMSwapfilePolicy.ToString() $hostProfileAssociatedWithVMHost = $this.GetHostProfileAssociatedWithVMHost($vmHost) $result.HostProfileName = $hostProfileAssociatedWithVMHost.Name if (![string]::IsNullOrEmpty($this.KmsClusterName)) { $kmsCluster = $this.GetKmsCluster() $result.KmsClusterName = if ($kmsCluster.Id -ne $vmHost.ExtensionData.Runtime.CryptoKeyId.ProviderId.Id) { $kmsCluster.Name } else { $null } } else { $result.KmsClusterName = $this.KmsClusterName } } } [DscResource()] class VMHostDnsSettings : VMHostBaseDSC { <# .DESCRIPTION List of domain name or IP address of the DNS Servers. #> [DscProperty()] [string[]] $Address <# .DESCRIPTION Indicates whether DHCP is used to determine DNS configuration. #> [DscProperty(Mandatory)] [bool] $Dhcp <# .DESCRIPTION Domain Name portion of the DNS name. For example, "vmware.com". #> [DscProperty(Mandatory)] [string] $DomainName <# .DESCRIPTION Host Name portion of DNS name. For example, "esx01". #> [DscProperty(Mandatory)] [string] $HostName <# .DESCRIPTION Desired value for the VMHost DNS Ipv6VirtualNicDevice. #> [DscProperty()] [string] $Ipv6VirtualNicDevice <# .DESCRIPTION Domain in which to search for hosts, placed in order of preference. #> [DscProperty()] [string[]] $SearchDomain <# .DESCRIPTION Desired value for the VMHost DNS VirtualNicDevice. #> [DscProperty()] [string] $VirtualNicDevice [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.UpdateDns($vmHost) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostDnsConfig = $vmHost.ExtensionData.Config.Network.DnsConfig $result = !(( $this.ShouldUpdateDscResourceSetting('Dhcp', $vmHostDnsConfig.Dhcp, $this.Dhcp), $this.ShouldUpdateDscResourceSetting('DomainName', $vmHostDnsConfig.DomainName, $this.DomainName), $this.ShouldUpdateDscResourceSetting('HostName', $vmHostDnsConfig.HostName, $this.HostName), $this.ShouldUpdateDscResourceSetting('Ipv6VirtualNicDevice', $vmHostDnsConfig.Ipv6VirtualNicDevice, $this.Ipv6VirtualNicDevice), $this.ShouldUpdateDscResourceSetting('VirtualNicDevice', $vmHostDnsConfig.VirtualNicDevice, $this.VirtualNicDevice), $this.ShouldUpdateArraySetting('Address', $vmHostDnsConfig.Address, $this.Address), $this.ShouldUpdateArraySetting('SearchDomain', $vmHostDnsConfig.SearchDomain, $this.SearchDomain) ) -Contains $true) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostDnsSettings] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostDnsSettings]::new() $vmHost = $this.GetVMHost() $vmHostDnsConfig = $vmHost.ExtensionData.Config.Network.DnsConfig $result.Name = $vmHost.Name $result.Server = $this.Server $result.Address = $vmHostDnsConfig.Address $result.Dhcp = $vmHostDnsConfig.Dhcp $result.DomainName = $vmHostDnsConfig.DomainName $result.HostName = $vmHostDnsConfig.HostName $result.Ipv6VirtualNicDevice = $vmHostDnsConfig.Ipv6VirtualNicDevice $result.SearchDomain = $vmHostDnsConfig.SearchDomain $result.VirtualNicDevice = $vmHostDnsConfig.VirtualNicDevice return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Updates the DNS Config of the VMHost with the Desired DNS Config. #> [void] UpdateDns($vmHost) { $dnsConfigArgs = @{ Address = $this.Address Dhcp = $this.Dhcp DomainName = $this.DomainName HostName = $this.HostName Ipv6VirtualNicDevice = $this.Ipv6VirtualNicDevice SearchDomain = $this.SearchDomain VirtualNicDevice = $this.VirtualNicDevice } $dnsConfig = New-DNSConfig @dnsConfigArgs $getViewParams = @{ Server = $this.Connection Id = $vmHost.ExtensionData.ConfigManager.NetworkSystem ErrorAction = 'Stop' Verbose = $false } $networkSystem = Get-View @getViewParams try { Update-DNSConfig -NetworkSystem $networkSystem -DnsConfig $dnsConfig } catch { throw "The DNS Config could not be updated: $($_.Exception.Message)" } } } [DscResource()] class VMHostFirewallRuleset : VMHostEntityBaseDSC { <# .DESCRIPTION Specifies the name of the firewall ruleset. #> [DscProperty(Key)] [string] $Name <# .DESCRIPTION Specifies whether the firewall ruleset should be enabled or disabled. #> [DscProperty()] [nullable[bool]] $Enabled <# .DESCRIPTION Specifies whether the firewall ruleset allows connections from any IP address. #> [DscProperty()] [nullable[bool]] $AllIP <# .DESCRIPTION Specifies the list of IP addresses. All IPv4 addresses are specified using dotted decimal format. For example '192.0.20.10'. IPv6 addresses are 128-bit addresses represented as eight fields of up to four hexadecimal digits. A colon separates each field (:). For example 2001:DB8:101::230:6eff:fe04:d9ff. The address can also consist of symbol '::' to represent multiple 16-bit groups of contiguous 0's only once in an address. #> [DscProperty()] [string[]] $IPAddresses hidden [string] $ModifyVMHostFirewallRulesetStateMessage = "Modifying the state of firewall ruleset {0} on VMHost {1}." hidden [string] $ModifyVMHostFirewallRulesetAllowedIPAddressesListMessage = "Modifying the allowed IP addresses list of firewall ruleset {0} on VMHost {1}." hidden [string] $CouldNotRetrieveFirewallSystemOfVMHostMessage = "Could not retrieve the FirewallSystem managed object of VMHost {0}. For more information: {1}" hidden [string] $CouldNotRetrieveFirewallRulesetMessage = "Could not retrieve firewall ruleset {0} from VMHost {1}. For more information: {2}" hidden [string] $CouldNotModifyVMHostFirewallRulesetStateMessage = "Could not modify the state of firewall ruleset {0} on VMHost {1}. For more information: {2}" hidden [string] $CouldNotModifyVMHostFirewallRulesetAllowedIPAddressesListMessage = "Could not modify the allowed IP addresses list of firewall ruleset {0} on VMHost {1}. For more information: {2}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $vmHostFirewallRuleset = $this.GetVMHostFirewallRuleset() if ($this.ShouldModifyVMHostFirewallRulesetState($vmHostFirewallRuleset)) { $this.ModifyVMHostFirewallRulesetState($vmHostFirewallRuleset) } if ($this.ShouldModifyVMHostFirewallRulesetAllowedIPAddressesList($vmHostFirewallRuleset)) { $vmHostFirewallSystem = $this.GetVMHostFirewallSystem() $this.ModifyVMHostFirewallRulesetAllowedIPAddressesList($vmHostFirewallSystem, $vmHostFirewallRuleset) } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $vmHostFirewallRuleset = $this.GetVMHostFirewallRuleset() $result = !($this.ShouldModifyVMHostFirewallRulesetState($vmHostFirewallRuleset) -or $this.ShouldModifyVMHostFirewallRulesetAllowedIPAddressesList($vmHostFirewallRuleset)) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostFirewallRuleset] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostFirewallRuleset]::new() $this.RetrieveVMHost() $vmHostFirewallRuleset = $this.GetVMHostFirewallRuleset() $this.PopulateResult($result, $vmHostFirewallRuleset) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the FirewallSystem of the specified VMHost. #> [PSObject] GetVMHostFirewallSystem() { try { $firewallSystem = Get-View -Server $this.Connection -Id $this.VMHost.ExtensionData.ConfigManager.FirewallSystem -ErrorAction Stop -Verbose:$false return $firewallSystem } catch { throw ($this.CouldNotRetrieveFirewallSystemOfVMHostMessage -f $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the firewall ruleset with the specified name on the specified VMHost. #> [PSObject] GetVMHostFirewallRuleset() { try { $vmHostFirewallRuleset = Get-VMHostFirewallException -Server $this.Connection -Name $this.Name -VMHost $this.VMHost -ErrorAction Stop -Verbose:$false return $vmHostFirewallRuleset } catch { throw ($this.CouldNotRetrieveFirewallRulesetMessage -f $this.Name, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Converts the passed string array containing IP networks in the following format: '10.20.120.12/22' to HostFirewallRulesetIpNetwork array, where the 'Network' is '10.23.120.12' and the 'PrefixLength' is '22'. #> [array] ConvertIPNetworksToHostFirewallRulesetIpNetworks($ipNetworks) { $hostFirewallRulesetIpNetworks = @() foreach ($ipNetwork in $ipNetworks) { $ipNetworkParts = $ipNetwork -Split '/' $hostFirewallRulesetIpNetwork = New-Object -TypeName VMware.Vim.HostFirewallRulesetIpNetwork $hostFirewallRulesetIpNetwork.Network = $ipNetworkParts[0] $hostFirewallRulesetIpNetwork.PrefixLength = $ipNetworkParts[1] $hostFirewallRulesetIpNetworks += $hostFirewallRulesetIpNetwork } return $hostFirewallRulesetIpNetworks } <# .DESCRIPTION Converts the passed HostFirewallRulesetIpNetwork array containing IP networks in the following format: 'Network' = '10.23.120.12' and 'PrefixLength' = '22' to string array, where each IP network is in the following format: '10.23.120.12/22'. #> [array] ConvertHostFirewallRulesetIpNetworksToIPNetworks($hostFirewallRulesetIpNetworks) { $ipNetworks = @() foreach ($hostFirewallRulesetIpNetwork in $hostFirewallRulesetIpNetworks) { $ipNetwork = $hostFirewallRulesetIpNetwork.Network + '/' + $hostFirewallRulesetIpNetwork.PrefixLength $ipNetworks += $ipNetwork } return $ipNetworks } <# .DESCRIPTION Checks if the current firewall ruleset state (enabled or disabled) is equal to the desired firewall ruleset state. #> [bool] ShouldModifyVMHostFirewallRulesetState($vmHostFirewallRuleset) { return $this.ShouldUpdateDscResourceSetting('Enabled', $vmHostFirewallRuleset.Enabled, $this.Enabled) } <# .DESCRIPTION Checks if the current firewall ruleset IP addresses allowed list is equal to the desired firewall ruleset IP addresses allowed list. #> [bool] ShouldModifyVMHostFirewallRulesetAllowedIPAddressesList($vmHostFirewallRuleset) { $vmHostFirewallRulesetAllowedHosts = $vmHostFirewallRuleset.ExtensionData.AllowedHosts $shouldModifyVMHostFirewallRulesetAllowedIPAddressesList = @( $this.ShouldUpdateDscResourceSetting('AllIP', $vmHostFirewallRulesetAllowedHosts.AllIp, $this.AllIP) ) if ($null -ne $this.IPAddresses) { $desiredIPAddresses = $this.IPAddresses -NotMatch '/' $desiredIPNetworks = $this.IPAddresses -Match '/' $shouldModifyVMHostFirewallRulesetAllowedIPAddressesList += $this.ShouldUpdateArraySetting( 'IPAddresses', $vmHostFirewallRulesetAllowedHosts.IpAddress, $desiredIPAddresses ) $shouldModifyVMHostFirewallRulesetAllowedIPAddressesList += $this.ShouldUpdateArraySetting( 'IPNetworks', $this.ConvertHostFirewallRulesetIpNetworksToIPNetworks($vmHostFirewallRulesetAllowedHosts.IpNetwork), $desiredIPNetworks ) } return ($shouldModifyVMHostFirewallRulesetAllowedIPAddressesList -Contains $true) } <# .DESCRIPTION Modifies the firewall ruleset state depending on the specified value (enables or disables the firewall ruleset). #> [void] ModifyVMHostFirewallRulesetState($vmHostFirewallRuleset) { $setVMHostFirewallExceptionParams = @{ Exception = $vmHostFirewallRuleset Enabled = $this.Enabled Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.ModifyVMHostFirewallRulesetStateMessage, @($vmHostFirewallRuleset.Name, $this.VMHost.Name)) Set-VMHostFirewallException @setVMHostFirewallExceptionParams } catch { throw ($this.CouldNotModifyVMHostFirewallRulesetStateMessage -f $vmHostFirewallRuleset.Name, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Modifies the firewall ruleset IP addresses allowed list. #> [void] ModifyVMHostFirewallRulesetAllowedIPAddressesList($vmHostFirewallSystem, $vmHostFirewallRuleset) { $vmHostFirewallRulesetSpec = New-Object -TypeName VMware.Vim.HostFirewallRulesetRulesetSpec $vmHostFirewallRulesetSpec.AllowedHosts = New-Object -TypeName VMware.Vim.HostFirewallRulesetIpList if ($null -ne $this.AllIP) { $vmHostFirewallRulesetSpec.AllowedHosts.AllIp = $this.AllIP } if ($null -ne $this.IPAddresses) { $desiredIPAddresses = $this.IPAddresses -NotMatch '/' $desiredIPNetworks = $this.IPAddresses -Match '/' $vmHostFirewallRulesetSpec.AllowedHosts.IpAddress = $desiredIPAddresses $vmHostFirewallRulesetSpec.AllowedHosts.IpNetwork = $this.ConvertIPNetworksToHostFirewallRulesetIpNetworks($desiredIPNetworks) } try { $this.WriteLogUtil('Verbose', $this.ModifyVMHostFirewallRulesetAllowedIPAddressesListMessage, @($vmHostFirewallRuleset.Name, $this.VMHost.Name)) Update-VMHostFirewallRuleset -VMHostFirewallSystem $vmHostFirewallSystem -VMHostFirewallRulesetId $vmHostFirewallRuleset.ExtensionData.Key -VMHostFirewallRulesetSpec $vmHostFirewallRulesetSpec } catch { throw ($this.CouldNotModifyVMHostFirewallRulesetAllowedIPAddressesListMessage -f $vmHostFirewallRuleset.Name, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHostFirewallRuleset) { $result.Server = $this.Connection.Name $result.VMHostName = $this.VMHost.Name $result.Name = $vmHostFirewallRuleset.Name $result.Enabled = $vmHostFirewallRuleset.Enabled $vmHostFirewallRulesetAllowedHosts = $vmHostFirewallRuleset.ExtensionData.AllowedHosts $result.AllIP = $vmHostFirewallRulesetAllowedHosts.AllIp $result.IPAddresses = $vmHostFirewallRulesetAllowedHosts.IpAddress + $this.ConvertHostFirewallRulesetIpNetworksToIPNetworks($vmHostFirewallRulesetAllowedHosts.IpNetwork) } } [DscResource()] class VMHostIScsiHba : VMHostIScsiHbaBaseDSC { <# .DESCRIPTION Specifies the name of the iSCSI Host Bus Adapter. #> [DscProperty(Key)] [string] $Name <# .DESCRIPTION Specifies the name for the VMHost Host Bus Adapter device. #> [DscProperty()] [string] $IScsiName hidden [string] $ConfigureIScsiHbaMessage = "Configuring iSCSI Host Bus Adapter {0} from VMHost {1}." hidden [string] $CouldNotConfigureIScsiHbaMessage = "Could not configure iSCSI Host Bus Adapter {0} from VMHost {1}. For more information: {2}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $iScsiHba = $this.GetIScsiHba($this.Name) $this.ConfigureIScsiHba($iScsiHba) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $iScsiHba = $this.GetIScsiHba($this.Name) $shouldConfigureiScsiHba = @( $this.ShouldModifyCHAPSettings($iScsiHba.AuthenticationProperties), $this.ShouldUpdateDscResourceSetting('IScsiName', $iScsiHba.IScsiName, $this.IScsiName) ) $result = !($shouldConfigureiScsiHba -Contains $true) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostIScsiHba] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostIScsiHba]::new() $this.RetrieveVMHost() $iScsiHba = $this.GetIScsiHba($this.Name) $this.PopulateResult($result, $iScsiHba) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Configures the CHAP properties and the iScsi name of the specified iSCSI Host Bus Adapter. #> [void] ConfigureIScsiHba($iScsiHba) { $setVMHostHbaParams = @{ IScsiHba = $iScsiHba Confirm = $false ErrorAction = 'Stop' Verbose = $false } $this.PopulateCmdletParametersWithCHAPSettings($setVMHostHbaParams) if (![string]::IsNullOrEmpty($this.IScsiName)) { $setVMHostHbaParams.IScsiName = $this.IScsiName } try { $this.WriteLogUtil('Verbose', $this.ConfigureIScsiHbaMessage, @($iScsiHba.Device, $this.VMHost.Name)) Set-VMHostHba @setVMHostHbaParams } catch { throw ($this.CouldNotConfigureIScsiHbaMessage -f $iScsiHba.Device, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $iScsiHba) { $result.Server = $this.Connection.Name $result.VMHostName = $this.VMHost.Name $result.Name = $iScsiHba.Device $result.IScsiName = $iScsiHba.IScsiName $result.ChapType = $iScsiHba.AuthenticationProperties.ChapType.ToString() $result.ChapName = [string] $iScsiHba.AuthenticationProperties.ChapName $result.MutualChapEnabled = $iScsiHba.AuthenticationProperties.MutualChapEnabled $result.MutualChapName = [string] $iScsiHba.AuthenticationProperties.MutualChapName $result.Force = $this.Force } } [DscResource()] class VMHostIScsiHbaTarget : VMHostIScsiHbaBaseDSC { <# .DESCRIPTION Specifies the address of the iSCSI Host Bus Adapter target. #> [DscProperty(Key)] [string] $Address <# .DESCRIPTION Specifies the TCP port of the iSCSI Host Bus Adapter target. #> [DscProperty(Key)] [int] $Port <# .DESCRIPTION Specifies the name of the iSCSI Host Bus Adapter of the iSCSI Host Bus Adapter target. #> [DscProperty(Key)] [string] $IScsiHbaName <# .DESCRIPTION Specifies the type of the iSCSI Host Bus Adapter target. #> [DscProperty(Key)] [IScsiHbaTargetType] $TargetType <# .DESCRIPTION Specifies whether the iSCSI Host Bus Adapter target should be present or absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION Specifies the iSCSI name of the iSCSI Host Bus Adapter target. It is required for Static iSCSI Host Bus Adapter targets. #> [DscProperty()] [string] $IScsiName <# .DESCRIPTION Indicates that the CHAP setting is inherited from the iSCSI Host Bus Adapter. #> [DscProperty()] [nullable[bool]] $InheritChap <# .DESCRIPTION Indicates that the Mutual CHAP setting is inherited from the iSCSI Host Bus Adapter. #> [DscProperty()] [nullable[bool]] $InheritMutualChap <# .DESCRIPTION Specifies the <Address>:<Port> that uniquely identifies an iSCSI Host Bus Adapter target. #> hidden [string] $IPEndPoint hidden [string] $CreateIScsiHbaTargetMessage = "Creating iSCSI Host Bus Adapter target with IP address {0} on iSCSI Host Bus Adapter device {1}." hidden [string] $ModifyIScsiHbaTargetMessage = "Modifying CHAP settings of iSCSI Host Bus Adapter target with IP address {0} on iSCSI Host Bus Adapter device {1}." hidden [string] $RemoveIScsiHbaTargetMessage = "Removing iSCSI Host Bus Adapter target with IP address {0} from iSCSI Host Bus Adapter device {1}." hidden [string] $CouldNotCreateIScsiHbaTargetMessage = "Could not create iSCSI Host Bus Adapter target with IP address {0} on iSCSI Host Bus Adapter device {1}. For more information: {2}" hidden [string] $CouldNotModifyIScsiHbaTargetMessage = "Could not modify CHAP settings of iSCSI Host Bus Adapter target with IP address {0} on iSCSI Host Bus Adapter device {1}. For more information: {2}" hidden [string] $CouldNotRemoveIScsiHbaTargetMessage = "Could not remove iSCSI Host Bus Adapter target with IP address {0} from iSCSI Host Bus Adapter device {1}. For more information: {2}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $this.IPEndPoint = $this.Address + ':' + $this.Port.ToString() $iScsiHba = $this.GetIScsiHba($this.IScsiHbaName) $iScsiHbaTarget = $this.GetIScsiHbaTarget($iScsiHba) if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $iScsiHbaTarget) { $this.NewIScsiHbaTarget($iScsiHba) } else { $this.ModifyIScsiHbaTarget($iScsiHbaTarget) } } else { if ($null -ne $iScsiHbaTarget) { $this.RemoveIScsiHbaTarget($iScsiHbaTarget) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $this.IPEndPoint = $this.Address + ':' + $this.Port.ToString() $iScsiHba = $this.GetIScsiHba($this.IScsiHbaName) $iScsiHbaTarget = $this.GetIScsiHbaTarget($iScsiHba) $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $iScsiHbaTarget) { $result = $false } else { $result = !$this.ShouldModifyCHAPSettings($iScsiHbaTarget.AuthenticationProperties, $this.InheritChap, $this.InheritMutualChap) } } else { $result = ($null -eq $iScsiHbaTarget) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostIScsiHbaTarget] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostIScsiHbaTarget]::new() $this.RetrieveVMHost() $this.IPEndPoint = $this.Address + ':' + $this.Port.ToString() $iScsiHba = $this.GetIScsiHba($this.IScsiHbaName) $iScsiHbaTarget = $this.GetIScsiHbaTarget($iScsiHba) $this.PopulateResult($result, $iScsiHbaTarget) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the iSCSI Host Bus Adapter target with the specified IPEndPoint for the specified iSCSI Host Bus Adapter if it exists. #> [PSObject] GetIScsiHbaTarget($iScsiHba) { $getIScsiHbaTargetParams = @{ Server = $this.Connection IScsiHba = $iScsiHba IPEndPoint = $this.IPEndPoint Type = $this.TargetType.ToString() ErrorAction = 'SilentlyContinue' Verbose = $false } return Get-IScsiHbaTarget @getIScsiHbaTargetParams } <# .DESCRIPTION Creates a new iSCSI Host Bus Adapter target of the specified type with the specified address for the specified iSCSI Host Bus Adapter. #> [void] NewIScsiHbaTarget($iScsiHba) { $newIScsiHbaTargetParams = @{ Server = $this.Connection Address = $this.Address Port = $this.Port IScsiHba = $iScsiHba Type = $this.TargetType.ToString() Confirm = $false ErrorAction = 'Stop' Verbose = $false } if ($this.TargetType -eq [IScsiHbaTargetType]::Static) { $newIScsiHbaTargetParams.IScsiName = $this.IScsiName } $this.PopulateCmdletParametersWithCHAPSettings($newIScsiHbaTargetParams, $this.InheritChap, $this.InheritMutualChap) try { $this.WriteLogUtil('Verbose', $this.CreateIScsiHbaTargetMessage, @($this.IPEndPoint, $this.IScsiHbaName)) New-IScsiHbaTarget @newIScsiHbaTargetParams } catch { throw ($this.CouldNotCreateIScsiHbaTargetMessage -f $this.IPEndPoint, $this.IScsiHbaName, $_.Exception.Message) } } <# .DESCRIPTION Modifies the CHAP settings of the specified iSCSI Host Bus Adapter target. #> [void] ModifyIScsiHbaTarget($iScsiHbaTarget) { $setIScsiHbaTargetParams = @{ Server = $this.Connection Target = $iScsiHbaTarget Confirm = $false ErrorAction = 'Stop' Verbose = $false } $this.PopulateCmdletParametersWithCHAPSettings($setIScsiHbaTargetParams, $this.InheritChap, $this.InheritMutualChap) try { $this.WriteLogUtil('Verbose', $this.ModifyIScsiHbaTargetMessage, @($this.IPEndPoint, $this.IScsiHbaName)) Set-IScsiHbaTarget @setIScsiHbaTargetParams } catch { throw ($this.CouldNotModifyIScsiHbaTargetMessage -f $this.IPEndPoint, $this.IScsiHbaName, $_.Exception.Message) } } <# .DESCRIPTION Removes the specified iSCSI Host Bus Adapter target from its iSCSI Host Bus Adapter. #> [void] RemoveIScsiHbaTarget($iScsiHbaTarget) { $removeIScsiHbaTargetParams = @{ Server = $this.Connection Target = $iScsiHbaTarget Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.RemoveIScsiHbaTargetMessage, @($this.IPEndPoint, $this.IScsiHbaName)) Remove-IScsiHbaTarget @removeIScsiHbaTargetParams } catch { throw ($this.CouldNotRemoveIScsiHbaTargetMessage -f $this.IPEndPoint, $this.IScsiHbaName, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $iScsiHbaTarget) { $result.Server = $this.Connection.Name $result.VMHostName = $this.VMHost.Name $result.IScsiHbaName = $this.IScsiHbaName $result.Force = $this.Force if ($null -ne $iScsiHbaTarget) { $result.Address = $iScsiHbaTarget.Address $result.Port = $iScsiHbaTarget.Port $result.TargetType = $iScsiHbaTarget.Type.ToString() $result.IScsiName = $iScsiHbaTarget.IScsiName $result.InheritChap = $iScsiHbaTarget.AuthenticationProperties.ChapInherited $result.ChapType = $iScsiHbaTarget.AuthenticationProperties.ChapType.ToString() $result.ChapName = [string] $iScsiHbaTarget.AuthenticationProperties.ChapName $result.InheritMutualChap = $iScsiHbaTarget.AuthenticationProperties.MutualChapInherited $result.MutualChapEnabled = $iScsiHbaTarget.AuthenticationProperties.MutualChapEnabled $result.MutualChapName = [string] $iScsiHbaTarget.AuthenticationProperties.MutualChapName $result.Ensure = [Ensure]::Present } else { $result.Address = $this.Address $result.Port = $this.Port $result.TargetType = $this.TargetType $result.IScsiName = $this.IScsiName $result.InheritChap = $this.InheritChap $result.ChapType = $this.ChapType $result.ChapName = $this.ChapName $result.InheritMutualChap = $this.InheritMutualChap $result.MutualChapEnabled = $this.MutualChapEnabled $result.MutualChapName = $this.MutualChapName $result.Ensure = [Ensure]::Absent } } } [DscResource()] class VMHostIScsiHbaVMKernelNic : VMHostEntityBaseDSC { <# .DESCRIPTION Specifies the name of the iSCSI Host Bus Adapter. #> [DscProperty(Key)] [string] $IScsiHbaName <# .DESCRIPTION Specifies the names of the VMKernel Network Adapters that should be bound/unbound to/from the specified iSCSI Host Bus Adapter. #> [DscProperty(Mandatory)] [string[]] $VMKernelNicNames <# .DESCRIPTION Value indicating if the VMKernel Network Adapters should be bound/unbound to/from the specified iSCSI Host Bus Adapter. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION Specifies whether to bind VMKernel Network Adapters to iSCSI Host Bus Adapter when VMKernel Network Adapters aren't compatible for iSCSI multipathing. Specifies whether to unbind VMKernel Network Adapters from iSCSI Host Bus Adapter when there're active sessions using the VMKernel Network Adapters. #> [DscProperty()] [nullable[bool]] $Force hidden [string] $IScsiDeviceType = 'iSCSI' hidden [string] $RetrieveIScsiHbaMessage = "Retrieving iSCSI Host Bus Adapter {0} from VMHost {1}." hidden [string] $RetrieveEsxCliInterfaceMessage = "Retrieving EsxCli interface for VMHost {0}." hidden [string] $RetrieveVMKernelNicsMessage = "Retrieving VMKernel Network Adapters {0} from VMHost {1}." hidden [string] $VMKernelNicAlreadyBoundMessage = "VMKernel Network Adapter {0} is already bound to iSCSI Host Bus Adapter {1} and will be ignored." hidden [string] $VMKernelNicAlreadyUnboundMessage = "VMKernel Network Adapter {0} is already unbound from iSCSI Host Bus Adapter {1} and will be ignored." hidden [string] $VMKernelNicBindMessage = "Binding VMKernel Network Adapter {0} to iSCSI Host Bus Adapter {1}." hidden [string] $VMKernelNicUnbindMessage = "Unbinding VMKernel Network Adapter {0} from iSCSI Host Bus Adapter {1}." hidden [string] $CouldNotRetrieveIScsiHbaMessage = "Could not retrieve iSCSI Host Bus Adapter {0} from VMHost {1}. For more information: {2}" hidden [string] $CouldNotRetrieveEsxCliInterfaceMessage = "Could not retrieve EsxCli interface for VMHost {0}. For more information: {1}" hidden [string] $CouldNotRetrieveVMKernelNicMessage = "VMKernel Network Adapter {0} could not be retrieved from VMHost {1} and will be ignored." hidden [string] $CouldNotBindVMKernelNicMessage = "Could not bind VMKernel Network Adapter {0} to iSCSI Host Bus Adapter {1}. For more information: {2}" hidden [string] $CouldNotUnbindVMKernelNicMessage = "Could not unbind VMKernel Network Adapter {0} from iSCSI Host Bus Adapter {1}. For more information: {2}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $esxCli = $this.GetEsxCli() $iScsiHba = $this.GetIScsiHba() $vmKernelNics = $this.GetVMKernelNetworkAdapters() $filteredVMKernelNics = $this.GetFilteredVMKernelNetworkAdapters($esxCli, $iScsiHba, $vmKernelNics) if ($this.Ensure -eq [Ensure]::Present) { $this.BindVMKernelNicsToIScsiHba($esxCli, $iScsiHba, $filteredVMKernelNics) } else { $this.UnbindVMKernelNicsToIScsiHba($esxCli, $iScsiHba, $filteredVMKernelNics) } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $esxCli = $this.GetEsxCli() $iScsiHba = $this.GetIScsiHba() $vmKernelNics = $this.GetVMKernelNetworkAdapters() $result = !$this.ShouldUpdateIScsiHbaBoundNics($esxCli, $iScsiHba, $vmKernelNics) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostIScsiHbaVMKernelNic] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $result = [VMHostIScsiHbaVMKernelNic]::new() $this.ConnectVIServer() $this.RetrieveVMHost() $esxCli = $this.GetEsxCli() $iScsiHba = $this.GetIScsiHba() $this.PopulateResult($result, $esxCli, $iScsiHba) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the EsxCli version 2 interface to ESXCLI for the specified VMHost. #> [PSObject] GetEsxCli() { try { $this.WriteLogUtil('Verbose', $this.RetrieveEsxCliInterfaceMessage, @($this.VMHost.Name)) $getEsxCliParams = @{ Server = $this.Connection VMHost = $this.VMHost V2 = $true ErrorAction = 'Stop' Verbose = $false } return Get-EsxCli @getEsxCliParams } catch { throw ($this.CouldNotRetrieveEsxCliInterfaceMessage -f $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the iSCSI Host Bus Adapter with the specified name from the specified VMHost. If the iSCSI Host Bus Adapter is not found, it throws an exception. #> [PSObject] GetIScsiHba() { try { $this.WriteLogUtil('Verbose', $this.RetrieveIScsiHbaMessage, @($this.IScsiHbaName, $this.VMHost.Name)) $getVMHostHbaParams = @{ Server = $this.Connection VMHost = $this.VMHost Device = $this.IScsiHbaName Type = $this.IScsiDeviceType ErrorAction = 'Stop' Verbose = $false } return Get-VMHostHba @getVMHostHbaParams } catch { throw ($this.CouldNotRetrieveIScsiHbaMessage -f $this.IScsiHbaName, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the VMKernel Network Adapters with the specified names from the specified VMHost. For every VMKernel Network Adapter that doesn't exist, a warning message is shown to the user without throwing an exception. #> [array] GetVMKernelNetworkAdapters() { $vmKernelNics = @() $this.WriteLogUtil('Verbose', $this.RetrieveVMKernelNicsMessage, @(($this.VMKernelNicNames -Join ', '), $this.VMHost.Name)) $getVMHostNetworkAdapterParams = @{ Server = $this.Connection VMHost = $this.VMHost VMKernel = $true ErrorAction = 'SilentlyContinue' Verbose = $false } $retrievedVMKernelNics = Get-VMHostNetworkAdapter @getVMHostNetworkAdapterParams foreach ($vmKernelNicName in $this.VMKernelNicNames) { $vmKernelNic = $retrievedVMKernelNics | Where-Object -FilterScript { $_.Name -eq $vmKernelNicName } if ($null -eq $vmKernelNic) { $this.WriteLogUtil('Warning', $this.CouldNotRetrieveVMKernelNicMessage, @($vmKernelNicName, $this.VMHost.Name)) } else { $vmKernelNics += $vmKernelNic } } return $vmKernelNics } <# .DESCRIPTION Returns the filtered VMKernel Network Adapters based on the value of the Ensure property. If Ensure is set to 'Present', it returns only those VMKernel Network Adapters that are currently not bound to the iSCSI Host Bus Adapter. The other VMKernel Network Adapters in the array are ignored because they're already bound to the iSCSI Host Bus Adapter. If Ensure is set to 'Absent', it returns only these VMKernel Network Adapters that are currently bound to the iSCSI Host Bus Adapter. The other VMKernel Network Adapters in the array are ignored because they're not bound to the iSCSI Host Bus Adapter. In both cases a warning message is shown to the user when a specific VMKernel Network Adapter is ignored. #> [array] GetFilteredVMKernelNetworkAdapters($esxCli, $iScsiHba, $vmKernelNics) { $filteredVMKernelNics = @() $boundVMKernelNics = Get-IScsiHbaBoundNics -EsxCli $esxCli -IScsiHbaName $iScsiHba.Device foreach ($vmKernelNic in $vmKernelNics) { $boundVMKernelNic = $boundVMKernelNics | Where-Object -FilterScript { $_.Vmknic -eq $vmKernelNic.Name } if ($this.Ensure -eq [Ensure]::Present -and $null -ne $boundVMKernelNic) { $this.WriteLogUtil('Warning', $this.VMKernelNicAlreadyBoundMessage, @($vmKernelNic.Name, $iScsiHba.Device)) continue } elseif ($this.Ensure -eq [Ensure]::Absent -and $null -eq $boundVMKernelNic) { $this.WriteLogUtil('Warning', $this.VMKernelNicAlreadyUnboundMessage, @($vmKernelNic.Name, $iScsiHba.Device)) continue } $filteredVMKernelNics += $vmKernelNic } return $filteredVMKernelNics } <# .DESCRIPTION Checks if VMKernel Network Adapters should be bound/unbound to/from the specified iSCSI Host Bus Adapter. If Ensure is set to 'Present', checks if all passed VMKernel Network Adapters are bound to the specified iSCSI Host Bus Adapter. If Ensure is set to 'Absent', checks if all passed VMKernel Network Adapters are unbound from the specified iSCSI Host Bus Adapter. #> [bool] ShouldUpdateIScsiHbaBoundNics($esxCli, $iScsiHba, $vmKernelNics) { $result = $false $boundVMKernelNics = Get-IScsiHbaBoundNics -EsxCli $esxCli -IScsiHbaName $iScsiHba.Device foreach ($vmKernelNic in $vmKernelNics) { $boundVMKernelNic = $boundVMKernelNics | Where-Object -FilterScript { $_.Vmknic -eq $vmKernelNic.Name } if ($this.Ensure -eq [Ensure]::Present -and $null -eq $boundVMKernelNic) { $result = $true break } elseif ($this.Ensure -eq [Ensure]::Absent -and $null -ne $boundVMKernelNic) { $result = $true break } } return $result } <# .DESCRIPTION Binds the specified VMKernel Network Adapters to the specified iSCSI Host Bus Adapter. #> [void] BindVMKernelNicsToIScsiHba($esxCli, $iScsiHba, $vmKernelNics) { foreach ($vmKernelNic in $vmKernelNics) { try { $this.WriteLogUtil('Verbose', $this.VMKernelNicBindMessage, @($vmKernelNic.Name, $iScsiHba.Device)) Update-IScsiHbaBoundNics -EsxCli $esxCli -IScsiHbaName $iScsiHba.Device -VMKernelNicName $vmKernelNic.Name -Operation 'Add' -Force $this.Force } catch { throw ($this.CouldNotBindVMKernelNicMessage -f $vmKernelNic.Name, $iScsiHba.Device, $_.Exception.Message) } } } <# .DESCRIPTION Unbinds the specified VMKernel Network Adapters from the specified iSCSI Host Bus Adapter. #> [void] UnbindVMKernelNicsToIScsiHba($esxCli, $iScsiHba, $vmKernelNics) { foreach ($vmKernelNic in $vmKernelNics) { try { $this.WriteLogUtil('Verbose', $this.VMKernelNicUnbindMessage, @($vmKernelNic.Name, $iScsiHba.Device)) Update-IScsiHbaBoundNics -EsxCli $esxCli -IScsiHbaName $iScsiHba.Device -VMKernelNicName $vmKernelNic.Name -Operation 'Remove' -Force $this.Force } catch { throw ($this.CouldNotUnbindVMKernelNicMessage -f $vmKernelNic.Name, $iScsiHba.Device, $_.Exception.Message) } } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $esxCli, $iScsiHba) { $result.Server = $this.Connection.Name $result.VMHostName = $this.VMHost.Name $result.IScsiHbaName = $iScsiHba.Device $result.Force = $this.Force $boundVMKernelNics = Get-IScsiHbaBoundNics -EsxCli $esxCli -IScsiHbaName $iScsiHba.Device if ($boundVMKernelNics.Length -gt 0) { $result.Ensure = [Ensure]::Present $result.VMKernelNicNames = $boundVMKernelNics.Vmknic } else { $result.Ensure = [Ensure]::Absent $result.VMKernelNicNames = $this.VMKernelNicNames } } } [DscResource()] class VMHostNtpSettings : VMHostBaseDSC { <# .DESCRIPTION List of domain name or IP address of the desired NTP Servers. #> [DscProperty()] [string[]] $NtpServer <# .DESCRIPTION Desired Policy of the VMHost 'ntpd' service activation. #> [DscProperty()] [ServicePolicy] $NtpServicePolicy = [ServicePolicy]::Unset hidden [string] $ServiceId = 'ntpd' [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.UpdateVMHostNtpServer($vmHost) $this.UpdateVMHostNtpServicePolicy($vmHost) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostNtpConfig = $vmHost.ExtensionData.Config.DateTimeInfo.NtpConfig $vmHostServices = $vmHost.ExtensionData.Config.Service $vmHostNtpService = $vmHostServices.Service | Where-Object -FilterScript { $_.Key -eq $this.ServiceId } $result = !(( $this.ShouldUpdateArraySetting('NtpServer', $vmHostNtpConfig.Server, $this.NtpServer), $this.ShouldUpdateDscResourceSetting('NtpServicePolicy', $vmHostNtpService.Policy.ToString(), $this.NtpServicePolicy.ToString()) ) -Contains $true) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostNtpSettings] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostNtpSettings]::new() $vmHost = $this.GetVMHost() $vmHostNtpConfig = $vmHost.ExtensionData.Config.DateTimeInfo.NtpConfig $vmHostServices = $vmHost.ExtensionData.Config.Service $vmHostNtpService = $vmHostServices.Service | Where-Object -FilterScript { $_.Key -eq $this.ServiceId } $result.Name = $vmHost.Name $result.Server = $this.Server $result.NtpServer = $vmHostNtpConfig.Server $result.NtpServicePolicy = $vmHostNtpService.Policy return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Updates the VMHost NTP Server with the desired NTP Server array. #> [void] UpdateVMHostNtpServer($vmHost) { $vmHostNtpConfig = $vmHost.ExtensionData.Config.DateTimeInfo.NtpConfig if (!$this.ShouldUpdateArraySetting('NtpServer', $vmHostNtpConfig.Server, $this.NtpServer)) { return } $dateTimeConfig = New-DateTimeConfig -NtpServer $this.NtpServer $getViewParams = @{ Server = $this.Connection Id = $vmHost.ExtensionData.ConfigManager.DateTimeSystem ErrorAction = 'Stop' Verbose = $false } $dateTimeSystem = Get-View @getViewParams Update-DateTimeConfig -DateTimeSystem $dateTimeSystem -DateTimeConfig $dateTimeConfig } <# .DESCRIPTION Updates the VMHost 'ntpd' Service Policy with the desired Service Policy. #> [void] UpdateVMHostNtpServicePolicy($vmHost) { $vmHostServices = $vmHost.ExtensionData.Config.Service $vmHostNtpService = $vmHostServices.Service | Where-Object -FilterScript { $_.Key -eq $this.ServiceId } if (!$this.ShouldUpdateDscResourceSetting('NtpServicePolicy', $vmHostNtpService.Policy.ToString(), $this.NtpServicePolicy.ToString())) { return } $getViewParams = @{ Server = $this.Connection Id = $vmHost.ExtensionData.ConfigManager.ServiceSystem ErrorAction = 'Stop' Verbose = $false } $serviceSystem = Get-View @getViewParams Update-ServicePolicy -ServiceSystem $serviceSystem -ServiceId $this.ServiceId -ServicePolicyValue $this.NtpServicePolicy.ToString().ToLower() } } [DscResource()] class VMHostPciPassthrough : VMHostRestartBaseDSC { <# .DESCRIPTION Specifies the Id of the PCI Device, composed of "bus:slot.function". #> [DscProperty(Key)] [string] $Id <# .DESCRIPTION Specifies whether passThru has been configured for this device. #> [DscProperty(Mandatory)] [bool] $Enabled [void] Set() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostPciPassthruSystem = $this.GetVMHostPciPassthruSystem($vmHost) $pciDevice = $this.GetPCIDevice($vmHostPciPassthruSystem) $this.EnsurePCIDeviceIsPassthruCapable($pciDevice) $this.EnsureVMHostIsInMaintenanceMode($vmHost) $this.UpdatePciPassthruConfiguration($vmHostPciPassthruSystem) $this.RestartVMHost($vmHost) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostPciPassthruSystem = $this.GetVMHostPciPassthruSystem($vmHost) $pciDevice = $this.GetPCIDevice($vmHostPciPassthruSystem) $this.EnsurePCIDeviceIsPassthruCapable($pciDevice) $result = !$this.ShouldUpdateDscResourceSetting('Enabled', $pciDevice.PassthruEnabled, $this.Enabled) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostPciPassthrough] Get() { try { $result = [VMHostPciPassthrough]::new() $result.Server = $this.Server $result.RestartTimeoutMinutes = $this.RestartTimeoutMinutes $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostPciPassthruSystem = $this.GetVMHostPciPassthruSystem($vmHost) $pciDevice = $this.GetPCIDevice($vmHostPciPassthruSystem) $this.EnsurePCIDeviceIsPassthruCapable($pciDevice) $result.Name = $vmHost.Name $result.Id = $pciDevice.Id $result.Enabled = $pciDevice.PassthruEnabled return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the PciPassthruSystem of the specified VMHost from the server. #> [PSObject] GetVMHostPciPassthruSystem($vmHost) { try { $vmHostPciPassthruSystem = Get-View -Server $this.Connection -Id $vmHost.ExtensionData.ConfigManager.PciPassthruSystem -ErrorAction Stop return $vmHostPciPassthruSystem } catch { throw "Could not retrieve the PciPassthruSystem of VMHost $($vmHost.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Retrieves the PCI Device with the specified Id from the server. #> [PSObject] GetPCIDevice($vmHostPciPassthruSystem) { $pciDevice = $vmHostPciPassthruSystem.PciPassthruInfo | Where-Object { $_.Id -eq $this.Id } if ($null -eq $pciDevice) { throw "The specified PCI Device $($this.Id) does not exist for VMHost $($this.Name)." } return $pciDevice } <# .DESCRIPTION Checks if the specified PCIDevice is Passthrough capable and if not, throws an exception. #> [void] EnsurePCIDeviceIsPassthruCapable($pciDevice) { if (!$pciDevice.PassthruCapable) { throw "Cannot configure PCI-Passthrough on incapable device $($pciDevice.Id)." } } <# .DESCRIPTION Performs an update on the specified PCI Device by changing its Passthru Enabled value. #> [void] UpdatePciPassthruConfiguration($vmHostPciPassthruSystem) { $vmHostPciPassthruConfig = New-Object VMware.Vim.HostPciPassthruConfig $vmHostPciPassthruConfig.Id = $this.Id $vmHostPciPassthruConfig.PassthruEnabled = $this.Enabled try { Update-PassthruConfig -VMHostPciPassthruSystem $vmHostPciPassthruSystem -VMHostPciPassthruConfig $vmHostPciPassthruConfig } catch { throw "The Update operation of PCI Device $($this.Id) failed with the following error: $($_.Exception.Message)" } } } [DscResource()] class VMHostPermission : BaseDSC { <# .DESCRIPTION Specifies the name of the Entity to which the Permission applies. #> [DscProperty(Key)] [string] $EntityName <# .DESCRIPTION Specifies the location of the Entity with name specified in 'EntityName' key property. Location consists of 0 or more Inventory Items. When the Entity is a Datacenter, a VMHost or a Datastore, the property is ignored. If the Entity is a Virtual Machine, a Resource Pool or a vApp and empty location is passed, the Entity should be located in the Root Resource Pool of the VMHost. Inventory Item names in the location are separated by '/'. Example location for a Datastore Inventory Item: ''. Example location for a Virtual Machine Inventory Item: 'MyResourcePoolOne/MyResourcePoolTwo/MyvApp'. #> [DscProperty(Key)] [string] $EntityLocation <# .DESCRIPTION Specifies the type of the Entity of the Permission. Valid Entity types are: 'Datacenter', 'VMHost', 'Datastore', 'VM', 'ResourcePool' and 'VApp'. #> [DscProperty(Key)] [EntityType] $EntityType <# .DESCRIPTION Specifies the name of the User to which the Permission applies. If the User is a Domain User, the Principal name should be in one of the following formats: '<Domain Name>/<User name>' or '<User name>@<Domain Name>'. Example Principal name for Domain User: 'MyDomain/MyDomainUser' or 'MyDomainUser@MyDomain'. #> [DscProperty(Key)] [string] $PrincipalName <# .DESCRIPTION Specifies the name of the Role to which the Permission applies. #> [DscProperty(Key)] [string] $RoleName <# .DESCRIPTION Specifies whether the Permission should be present or absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION Specifies whether to propagate the Permission to the child Inventory Items. #> [DscProperty()] [nullable[bool]] $Propagate hidden [string] $CreatePermissionMessage = "Creating Permission for Entity {0}, Principal {1} and Role {2} on VMHost {3}." hidden [string] $ModifyPermissionMessage = "Modifying Permission for Entity {0} and Principal {1} on VMHost {2}." hidden [string] $RemovePermissionMessage = "Removing Permission for Entity {0}, Principal {1} and Role {2} on VMHost {3}." hidden [string] $CouldNotRetrieveRootResourcePoolMessage = "Could not retrieve Root Resource Pool from VMHost {0}. For more information: {1}" hidden [string] $InvalidEntityLocationMessage = "Location {0} for Entity {1} on VMHost {2} is not valid." hidden [string] $CouldNotIdentifyVMMessage = "Could not uniquely identify VM with name {0} on VMHost {1}. {2} VMs with this name exist on the VMHost." hidden [string] $CouldNotFindEntityMessage = "Entity {0} of type {1} was not found on VMHost {2}." hidden [string] $CouldNotRetrievePrincipalMessage = "Could not retrieve Principal {0} from VMHost {1}. For more information: {2}" hidden [string] $CouldNotRetrieveRoleMessage = "Could not retrieve Role from VMHost {1}. For more information: {2}" hidden [string] $CouldNotCreatePermissionMessage = "Could not create Permission for Entity {0}, Principal {1} and Role {2} on VMHost {3}. For more information: {4}" hidden [string] $CouldNotModifyPermissionMessage = "Could not modify Permission for Entity {0} and Principal {1} on VMHost {2}. For more information: {3}" hidden [string] $CouldNotRemovePermissionMessage = "Could not remove Permission for Entity {0}, Principal {1} and Role {2} on VMHost {3}. For more information: {4}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsESXi() $foundEntityLocation = $this.GetEntityLocation() $entity = $this.GetEntity($foundEntityLocation) $vmHostPrincipal = $this.GetVMHostPrincipal() $vmHostPermission = $this.GetVMHostPermission($entity, $vmHostPrincipal) if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $vmHostPermission) { $this.NewVMHostPermission($entity, $vmHostPrincipal) } else { $this.ModifyVMHostPermission($vmHostPermission) } } else { if ($null -ne $vmHostPermission) { $this.RemoveVMHostPermission($vmHostPermission) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsESXi() $foundEntityLocation = $this.GetEntityLocation() $entity = $this.GetEntity($foundEntityLocation) $vmHostPrincipal = $this.GetVMHostPrincipal() $vmHostPermission = $this.GetVMHostPermission($entity, $vmHostPrincipal) $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $vmHostPermission) { $result = $false } else { $result = !$this.ShouldModifyVMHostPermission($vmHostPermission) } } else { $result = ($null -eq $vmHostPermission) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostPermission] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostPermission]::new() $this.EnsureConnectionIsESXi() $foundEntityLocation = $this.GetEntityLocation() $entity = $this.GetEntity($foundEntityLocation) $vmHostPrincipal = $this.GetVMHostPrincipal() $vmHostPermission = $this.GetVMHostPermission($entity, $vmHostPrincipal) $this.PopulateResult($result, $vmHostPermission) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the Root Resource Pool from the VMHost. #> [PSObject] GetRootResourcePool() { try { $vmHost = Get-VMHost -Server $this.Connection -ErrorAction Stop -Verbose:$false $rootResourcePool = Get-ResourcePool -Server $this.Connection -ErrorAction Stop -Verbose:$false | Where-Object -FilterScript { $_.ParentId -eq $vmHost.Id } return $rootResourcePool } catch { throw ($this.CouldNotRetrieveRootResourcePoolMessage -f $this.Connection.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the location of the Entity with the specified name on the VMHost if it exists. For VMs, Resource Pools and vApps if Ensure is 'Present' and the location is not found, an exception is thrown. If Ensure is 'Absent' and the location is not found, $null is returned. #> [PSObject] GetEntityLocation() { $foundEntityLocation = $null if ( $this.EntityType -eq [EntityType]::Datacenter -or $this.EntityType -eq [EntityType]::VMHost -or $this.EntityType -eq [EntityType]::Datastore ) { # The location is not needed to identify the Entity when it is a Datacenter, VMHost or a Datastore. $foundEntityLocation = $null } else { $rootResourcePool = $this.GetRootResourcePool() if ([string]::IsNullOrEmpty($this.EntityLocation)) { # Special case where the Entity location does not contain any Inventory Items. So the Root Resource Pool is the location for the Entity. $foundEntityLocation = $rootResourcePool } elseif ($this.EntityLocation -NotMatch '/') { # Special case where the Entity location is just one Resource Pool or vApp. On VMHosts the vApps are also retrieved with the Get-ResourcePool cmdlet. $foundEntityLocation = Get-ResourcePool -Server $this.Connection -Name $this.EntityLocation -Location $rootResourcePool -ErrorAction SilentlyContinue -Verbose:$false | Where-Object -FilterScript { $_.ParentId -eq $rootResourcePool.Id } } else { $entityLocationItems = $this.EntityLocation -Split '/' # Reverses the Entity location items so that we can start from the bottom and go to the top of the Inventory. [array]::Reverse($entityLocationItems) $entityLocationName = $entityLocationItems[0] $foundEntityLocations = Get-Inventory -Server $this.Connection -Name $entityLocationName -Location $rootResourcePool -ErrorAction SilentlyContinue -Verbose:$false # Removes the name of the Entity location from the Entity location items array as we already retrieved it. $entityLocationItems = $entityLocationItems[1..($entityLocationItems.Length - 1)] <# For every found Entity Location with the specified name we start to go up through the parents to check if the Entity Location is valid. If one of the Parents does not meet the criteria of the Entity location, we continue with the next found Entity location. If we find a valid Entity location we stop iterating through the Entity locations and mark it as the found Entity location. #> foreach ($entityLocation in $foundEntityLocations) { $foundEntityLocationAsViewObject = Get-View -Server $this.Connection -Id $entityLocation.Id -Verbose:$false -Property Parent $validEntityLocation = $true foreach ($entityLocationItem in $entityLocationItems) { $foundEntityLocationAsViewObject = Get-View -Server $this.Connection -Id $foundEntityLocationAsViewObject.Parent -Verbose:$false -Property Name, Parent if ($foundEntityLocationAsViewObject.Name -ne $entityLocationItem) { $validEntityLocation = $false break } } if ($validEntityLocation) { $foundEntityLocation = $entityLocation break } } } $exceptionMessage = $this.InvalidEntityLocationMessage -f $this.EntityLocation, $this.EntityName, $this.Connection.Name $this.EnsureCorrectBehaviourIfTheEntityIsNotFound($foundEntityLocation, $exceptionMessage) } return $foundEntityLocation } <# .DESCRIPTION Retrieves the Entity with the specified name from the specified location on the VMHost if it exists. For VMs, Resource Pools and vApps if Ensure is 'Present' and the Entity is not found, an exception is thrown. If Ensure is 'Absent' and the Entity is not found, $null is returned. #> [PSObject] GetEntity($entityLocation) { $entity = $null if ($this.EntityType -eq [EntityType]::Datacenter) { # Each VMHost has only one Datacenter, so the name is not needed to retrieve it. $entity = Get-Datacenter -Server $this.Connection -ErrorAction SilentlyContinue -Verbose:$false } elseif ($this.EntityType -eq [EntityType]::VMHost) { # If the Entity is a VMHost, the Entity name and location are ignored because the Connection is directly to an ESXi host. $entity = Get-VMHost -Server $this.Connection -ErrorAction SilentlyContinue -Verbose:$false } elseif ($this.EntityType -eq [EntityType]::Datastore) { # If the Entity is a Datastore, the Entity location is ignored because the name uniquely identifies the Datastore. $entity = Get-Datastore -Server $this.Connection -Name $this.EntityName -ErrorAction SilentlyContinue -Verbose:$false } elseif ($this.EntityType -eq [EntityType]::VM) { <# If the Entity is a VM, the Entity location is either a Resource Pool or a vApp where the VM is placed. If the VMHost is managed by a vCenter, there is a special case where two VMs could be created with the same name on the same VMHost but on different vCenter folders. And this way the VM could not be uniquely identified on the VMHost. #> $entity = Get-VM -Server $this.Connection -Name $this.EntityName -Location $entityLocation -ErrorAction SilentlyContinue -Verbose:$false # Only throw an exception if Ensure is 'Present', otherwise ignore that multiple VMs were found. if ($entity.Length -gt 1 -and $this.Ensure -eq [Ensure]::Present) { throw ($this.CouldNotIdentifyVMMessage -f $this.EntityName, $this.Connection.Name, $entity.Length) } } else { <# If the Entity is a Resource Pool or vApp, the Entity location is either a Resource Pool or a vApp where the Entity is placed. For a specific Resource Pool or vApp, the name does not uniquely identify the Entity because there can be other Resource Pools or vApps below in the hierarchy with the same name placed in the Entity location. So additional filtering is needed to verify that the Entity is directly placed in the specified Entity location. #> $entity = Get-ResourcePool -Server $this.Connection -Name $this.EntityName -Location $entityLocation -ErrorAction SilentlyContinue -Verbose:$false | Where-Object -FilterScript { $_.ParentId -eq $entityLocation.Id } } $exceptionMessage = $this.CouldNotFindEntityMessage -f $this.EntityName, $this.EntityType.ToString(), $this.Connection.Name $this.EnsureCorrectBehaviourIfTheEntityIsNotFound($entity, $exceptionMessage) return $entity } <# .DESCRIPTION Retrieves the Principal with the specified name from the VMHost if it exists. If the name contains '\' or '@', it means that the Principal is part of a Domain, so the search for the Principal should be done by filtering by Domain. If Ensure is 'Present' and the Principal is not found, an exception is thrown. If Ensure is 'Absent' and the Principal is not found, $null is returned. #> [PSObject] GetVMHostPrincipal() { $getVIAccountParams = @{ Server = $this.Connection Verbose = $false } # If the Principal is a Domain User, we should extact the Domain and User names from the Principal name. if ($this.PrincipalName -Match '\\') { $principalNameParts = $this.PrincipalName -Split '\\' $domainName = $principalNameParts[0] $username = $principalNameParts[1] $getVIAccountParams.Domain = $domainName $getVIAccountParams.User = $true $getVIAccountParams.Id = $username } elseif ($this.PrincipalName -Match '@') { $principalNameParts = $this.PrincipalName -Split '@' $username = $principalNameParts[0] $domainName = $principalNameParts[1] $getVIAccountParams.Domain = $domainName $getVIAccountParams.User = $true $getVIAccountParams.Id = $username } else { $getVIAccountParams.Id = $this.PrincipalName } if ($this.Ensure -eq [Ensure]::Absent) { $getVIAccountParams.ErrorAction = 'SilentlyContinue' return Get-VIAccount @getVIAccountParams } else { try { $getVIAccountParams.ErrorAction = 'Stop' $vmHostPrincipal = Get-VIAccount @getVIAccountParams return $vmHostPrincipal } catch { throw ($this.CouldNotRetrievePrincipalMessage -f $this.PrincipalName, $this.Connection.Name, $_.Exception.Message) } } } <# .DESCRIPTION Retrieves the Permission applied to the specified Entity and Principal from the VMHost if it exists. If one of the Entity and Principal parameters is $null, $null is returned. #> [PSObject] GetVMHostPermission($entity, $vmHostPrincipal) { if ($null -eq $entity -or $null -eq $vmHostPrincipal) { return $null } return Get-VIPermission -Server $this.Connection -Entity $entity -Principal $vmHostPrincipal -ErrorAction SilentlyContinue -Verbose:$false } <# .DESCRIPTION Retrieves the Role with the specified name from the VMHost if it exists. Otherwise it throws an exception. #> [PSObject] GetVMHostRole() { try { $vmHostRole = Get-VIRole -Server $this.Connection -Name $this.RoleName -ErrorAction Stop -Verbose:$false return $vmHostRole } catch { throw ($this.CouldNotRetrieveRoleMessage -f $this.RoleName, $this.Connection.Name, $_.Exception.Message) } } <# .DESCRIPTION Ensures the correct behaviour if the Entity is not found based on the passed Ensure value. If Ensure is 'Present' and the Entity is not found, the method should throw an exception. #> [void] EnsureCorrectBehaviourIfTheEntityIsNotFound($entity, $exceptionMessage) { if ($null -eq $entity) { if ($this.Ensure -eq [Ensure]::Present) { throw $exceptionMessage } } } <# .DESCRIPTION Checks if the specified Permission should be modified. The Permission should be modified if the desired Role is different from the current one or if the Propagate behaviour should be different. #> [bool] ShouldModifyVMHostPermission($vmHostPermission) { $shouldModifyVMHostPermission = @( $this.ShouldUpdateDscResourceSetting('RoleName', [string] $vmHostPermission.Role, $this.RoleName), $this.ShouldUpdateDscResourceSetting('Propagate', $vmHostPermission.Propagate, $this.Propagate) ) return ($shouldModifyVMHostPermission -Contains $true) } <# .DESCRIPTION Creates a new Permission and applies it to the specified Entity, Principal and Role. #> [void] NewVMHostPermission($entity, $vmHostPrincipal) { $vmHostRole = $this.GetVMHostRole() $newVIPermissionParams = @{ Server = $this.Connection Entity = $entity Principal = $vmHostPrincipal Role = $vmHostRole Confirm = $false ErrorAction = 'Stop' Verbose = $false } if ($null -ne $this.Propagate) { $newVIPermissionParams.Propagate = $this.Propagate } try { $this.WriteLogUtil('Verbose', $this.CreatePermissionMessage, @($entity.Name, $vmHostPrincipal.Name, $vmHostRole.Name, $this.Connection.Name)) New-VIPermission @newVIPermissionParams } catch { throw ($this.CouldNotCreatePermissionMessage -f $entity.Name, $vmHostPrincipal.Name, $vmHostRole.Name, $this.Connection.Name, $_.Exception.Message) } } <# .DESCRIPTION Modifies the properties of the specified Permission. Changes the Role if the desired one is not the same as the current one. It also changes the propagate behaviour of the Permission if the 'Propagate' property is specified. #> [void] ModifyVMHostPermission($vmHostPermission) { $setVIPermissionParams = @{ Server = $this.Connection Permission = $vmHostPermission Confirm = $false ErrorAction = 'Stop' Verbose = $false } if ($vmHostPermission.Role -ne $this.RoleName) { $vmHostRole = $this.GetVMHostRole() $setVIPermissionParams.Role = $vmHostRole } if ($null -ne $this.Propagate) { $setVIPermissionParams.Propagate = $this.Propagate } try { $this.WriteLogUtil('Verbose', $this.ModifyPermissionMessage, @($vmHostPermission.Entity.Name, $vmHostPermission.Principal, $this.Connection.Name)) Set-VIPermission @setVIPermissionParams } catch { throw ($this.CouldNotModifyPermissionMessage -f $vmHostPermission.Entity.Name, $vmHostPermission.Principal, $this.Connection.Name, $_.Exception.Message) } } <# .DESCRIPTION Removes the specified Permission. #> [void] RemoveVMHostPermission($vmHostPermission) { try { $this.WriteLogUtil('Verbose', $this.RemovePermissionMessage, @($vmHostPermission.Entity.Name, $vmHostPermission.Principal, $vmHostPermission.Role, $this.Connection.Name)) $vmHostPermission | Remove-VIPermission -Confirm:$false -ErrorAction Stop -Verbose:$false } catch { throw ($this.CouldNotRemovePermissionMessage -f $vmHostPermission.Entity.Name, $vmHostPermission.Principal, $vmHostPermission.Role, $this.Connection.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHostPermission) { $result.Server = $this.Connection.Name $result.EntityLocation = $this.EntityLocation $result.EntityType = $this.EntityType if ($null -ne $vmHostPermission) { $result.EntityName = $vmHostPermission.Entity.Name $result.PrincipalName = $vmHostPermission.Principal $result.RoleName = $vmHostPermission.Role $result.Ensure = [Ensure]::Present $result.Propagate = $vmHostPermission.Propagate } else { $result.EntityName = $this.EntityName $result.PrincipalName = $this.PrincipalName $result.RoleName = $this.RoleName $result.Ensure = [Ensure]::Absent $result.Propagate = $this.Propagate } } } [DscResource()] class VMHostPowerPolicy : VMHostBaseDSC { <# .DESCRIPTION Specifies the Power Management Policy for the specified VMHost. #> [DscProperty(Mandatory)] [PowerPolicy] $PowerPolicy [void] Set() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostPowerSystem = $this.GetVMHostPowerSystem($vmHost) $this.UpdatePowerPolicy($vmHostPowerSystem) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() $currentPowerPolicy = $vmHost.ExtensionData.Config.PowerSystemInfo.CurrentPolicy $result = !$this.ShouldUpdateDscResourceSetting('PowerPolicy', $currentPowerPolicy.Key, $this.PowerPolicy) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostPowerPolicy] Get() { try { $result = [VMHostPowerPolicy]::new() $result.Server = $this.Server $this.ConnectVIServer() $vmHost = $this.GetVMHost() $currentPowerPolicy = $vmHost.ExtensionData.Config.PowerSystemInfo.CurrentPolicy $result.Name = $vmHost.Name $result.PowerPolicy = $currentPowerPolicy.Key return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the Power System of the specified VMHost from the server. #> [PSObject] GetVMHostPowerSystem($vmHost) { try { $vmHostPowerSystem = Get-View -Server $this.Connection -Id $vmHost.ExtensionData.ConfigManager.PowerSystem -ErrorAction Stop return $vmHostPowerSystem } catch { throw "Could not retrieve the Power System of VMHost $($vmHost.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Performs an update on the Power Management Policy of the specified VMHost. #> [void] UpdatePowerPolicy($vmHostPowerSystem) { try { Update-PowerPolicy -VMHostPowerSystem $vmHostPowerSystem -PowerPolicy $this.PowerPolicy } catch { throw "The Power Policy of VMHost $($this.Name) could not be updated: $($_.Exception.Message)" } } } [DscResource()] class VMHostRole : BaseDSC { <# .DESCRIPTION Specifies the name of the Role on the VMHost. #> [DscProperty(Key)] [string] $Name <# .DESCRIPTION Specifies whether the Role on the VMHost should be present or absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION Specifies the ids of the Privileges for the Role on the VMHost. The Privilege ids should be in the following format: '<Privilege Group id>.<Privilege Item id>'. Exampe Privilege id: 'VirtualMachine.Inventory.Create' where 'VirtualMachine.Inventory' is the Privilege Group id and 'Create' is the id of the Privilege item. #> [DscProperty()] [string[]] $PrivilegeIds hidden [string] $CreateRoleMessage = "Creating Role {0} on VMHost {1}." hidden [string] $CreateRoleWithPrivilegesMessage = "Creating Role {0} with Privileges {1} on VMHost {2}." hidden [string] $ModifyPrivilegesOfRoleMessage = "Modifying Privileges of Role {0} on VMHost {1}." hidden [string] $RemoveRoleMessage = "Removing Role {0} on VMHost {1}." hidden [string] $CouldNotFindPrivilegeMessage = "The passed Privilege {0} was not found and it will be ignored." hidden [string] $CouldNotRetrieveRolePrivilegesMessage = "Could not retrieve Privilege {0} of Role {1}. For more information: {2}" hidden [string] $CouldNotCreateRoleMessage = "Could not create Role {0} on VMHost {1}. For more information: {2}" hidden [string] $CouldNotCreateRoleWithPrivilegesMessage = "Could not create Role {0} with Privileges {1} on VMHost {2}. For more information: {3}" hidden [string] $CouldNotModifyPrivilegesOfRoleMessage = "Could not modify Privileges of Role {0} on VMHost {1}. For more information: {2}" hidden [string] $CouldNotRemoveRoleMessage = "Could not remove Role {0} on VMHost {1}. For more information: {2}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsESXi() $vmHostRole = $this.GetVMHostRole() if ($this.Ensure -eq [Ensure]::Present) { $desiredPrivileges = $this.GetPrivileges() if ($null -eq $vmHostRole) { if ($desiredPrivileges.Length -gt 0) { $this.NewVMHostRole($desiredPrivileges) } else { $this.NewVMHostRole() } } else { $currentPrivileges = $this.GetRolePrivileges($vmHostRole, $desiredPrivileges) $this.ModifyPrivilegesOfVMHostRole($vmHostRole, $currentPrivileges, $desiredPrivileges) } } else { if ($null -ne $vmHostRole) { $this.RemoveVMHostRole($vmHostRole) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsESXi() $vmHostRole = $this.GetVMHostRole() $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $vmHostRole) { $result = $false } else { $desiredPrivileges = $this.GetPrivileges() $desiredPrivilegeIds = if ($desiredPrivileges.Length -eq 0) { $null } else { $desiredPrivileges.Id } $result = !$this.ShouldUpdateArraySetting('PrivilegeList', $vmHostRole.PrivilegeList, $desiredPrivilegeIds) } } else { $result = ($null -eq $vmHostRole) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostRole] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostRole]::new() $this.EnsureConnectionIsESXi() $vmHostRole = $this.GetVMHostRole() $this.PopulateResult($result, $vmHostRole) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the Role with the specified name on the VMHost if it exists, otherwise returns $null. #> [PSObject] GetVMHostRole() { return Get-VIRole -Server $this.Connection -Name $this.Name -ErrorAction SilentlyContinue -Verbose:$false } <# .DESCRIPTION Retrieves the Privileges with the specified ids on the VMHost if they exist. For every Privilege that does not exist, a warning message is shown to the user without throwing an exception. #> [array] GetPrivileges() { $privileges = @() foreach ($privilegeId in $this.PrivilegeIds) { $privilege = Get-VIPrivilege -Server $this.Connection -Id $privilegeId -ErrorAction SilentlyContinue -Verbose:$false if ($null -eq $privilege) { $this.WriteLogUtil('Warning', $this.CouldNotFindPrivilegeMessage, @($privilegeId)) } else { $privileges += $privilege } } return $privileges } <# .DESCRIPTION Retrieves the Privileges of the Role on the VMHost. #> [array] GetRolePrivileges($vmHostRole, $desiredPrivileges) { $rolePrivileges = @() foreach ($privilegeId in $vmHostRole.PrivilegeList) { <# Here we can check if the desired Privilege list already contains the Role Privilege and this way we can skip the server call because the Privilege object is already available in the array of Privileges. #> if ($desiredPrivileges.Length -gt 0 -and $desiredPrivileges.Id.Contains($privilegeId)) { $rolePrivileges += ($desiredPrivileges | Where-Object -FilterScript { $_.Id -eq $privilegeId }) } else { try { $rolePrivilige = Get-VIPrivilege -Server $this.Connection -Id $privilegeId -ErrorAction Stop -Verbose:$false $rolePrivileges += $rolePrivilige } catch { throw ($this.CouldNotRetrieveRolePrivilegesMessage -f $privilegeId, $vmHostRole.Name, $_.Exception.Message) } } } return $rolePrivileges } <# .DESCRIPTION Creates a new Role with the specified name on the VMHost. #> [void] NewVMHostRole() { try { $this.WriteLogUtil('Verbose', $this.CreateRoleMessage, @($this.Name, $this.Connection.Name)) New-VIRole -Server $this.Connection -Name $this.Name -Confirm:$false -ErrorAction Stop -Verbose:$false } catch { throw ($this.CouldNotCreateRoleMessage -f $this.Name, $this.Connection.Name, $_.Exception.Message) } } <# .DESCRIPTION Creates a new Role with the specified name on the VMHost and applies the provided Privileges. #> [void] NewVMHostRole($desiredPrivileges) { $desiredPrivilegeIds = [string]::Join(', ', $desiredPrivileges.Id) try { $this.WriteLogUtil('Verbose', $this.CreateRoleWithPrivilegesMessage, @($this.Name, $desiredPrivilegeIds, $this.Connection.Name)) New-VIRole -Server $this.Connection -Name $this.Name -Privilege $desiredPrivileges -Confirm:$false -ErrorAction Stop -Verbose:$false } catch { throw ($this.CouldNotCreateRoleWithPrivilegesMessage -f $this.Name, $desiredPrivilegeIds, $this.Connection.Name, $_.Exception.Message) } } <# .DESCRIPTION Modifies the Privileges of the Role on the VMHost. The 'Set-VIRole' cmdlet has two parameters for Privileges - 'AddPrivilege' and 'RemovePrivilege'. So based on the provided desired Privileges we need to add those of them that are not yet Privileges of the Role and also remove existing ones from the Privilege list of the Role because they are not specified as desired. #> [void] ModifyPrivilegesOfVMHostRole($vmHostRole, $currentPrivileges, $desiredPrivileges) { $setVIRoleParams = @{ Server = $this.Connection Role = $vmHostRole Confirm = $false ErrorAction = 'Stop' Verbose = $false } $privilegesToAdd = @() $privilegesToRemove = @() <# If the Role does not have Privileges, it means that all desired Privileges need to be marked as Privileges to add. Otherwise Privileges to add are those that are not present in the Privilege list of the Role and Privileges to remove are those that are not present in the desired Privilege list. #> if ($currentPrivileges.Length -eq 0) { $privilegesToAdd = $desiredPrivileges } else { $privilegesToAdd = $desiredPrivileges | Where-Object -FilterScript { $currentPrivileges -NotContains $_ } $privilegesToRemove = $currentPrivileges | Where-Object -FilterScript { $desiredPrivileges -NotContains $_ } } <# If the Privileges to add array is empty, it means that only removal of Privileges is needed. Otherwise, we first add all the specified Privileges that are not present in the list of Privileges of the Role and after that check if there are Privileges to remove from that list. The 'AddPrivilege' entry needs to be removed from the params hashtable because 'AddPrivilege' and 'RemovePrivilege' parameters of 'Set-VIRole' cmdlet are in different parameter sets, so two cmdlet invocations need to be made. #> try { $this.WriteLogUtil('Verbose', $this.ModifyPrivilegesOfRoleMessage, @($vmHostRole.Name, $this.Connection.Name)) if ($privilegesToAdd.Length -eq 0) { $setVIRoleParams.RemovePrivilege = $privilegesToRemove Set-VIRole @setVIRoleParams } else { $setVIRoleParams.AddPrivilege = $privilegesToAdd Set-VIRole @setVIRoleParams if ($privilegesToRemove.Length -gt 0) { $setVIRoleParams.Remove('AddPrivilege') $setVIRoleParams.RemovePrivilege = $privilegesToRemove Set-VIRole @setVIRoleParams } } } catch { throw ($this.CouldNotModifyPrivilegesOfRoleMessage -f $vmHostRole.Name, $this.Connection.Name, $_.Exception.Message) } } <# .DESCRIPTION Removes the Role on the VMHost. All Permissions associated with the Role will be removed as well. #> [void] RemoveVMHostRole($vmHostRole) { try { $this.WriteLogUtil('Verbose', $this.RemoveRoleMessage, @($vmHostRole.Name, $this.Connection.Name)) $vmHostRole | Remove-VIRole -Server $this.Connection -Force -Confirm:$false -ErrorAction Stop -Verbose:$false } catch { throw ($this.CouldNotRemoveRoleMessage -f $vmHostRole.Name, $this.Connection.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHostRole) { $result.Server = $this.Connection.Name if ($null -ne $vmHostRole) { $result.Name = $vmHostRole.Name $result.Ensure = [Ensure]::Present $result.PrivilegeIds = $vmHostRole.PrivilegeList } else { $result.Name = $this.Name $result.Ensure = [Ensure]::Absent $result.PrivilegeIds = $this.PrivilegeIds } } } [DscResource()] class VMHostSatpClaimRule : EsxCliBaseDSC { <# .DESCRIPTION Value indicating if the SATP Claim Rule should be Present or Absent. #> [DscProperty(Mandatory)] [Ensure] $Ensure <# .DESCRIPTION Name of the SATP Claim Rule. #> [DscProperty(Key)] [string] $RuleName <# .DESCRIPTION PSP options for the SATP Claim Rule. #> [DscProperty()] [string] $PSPOptions <# .DESCRIPTION Transport Property of the Satp Claim Rule. #> [DscProperty()] [string] $Transport <# .DESCRIPTION Description string to set when adding the SATP Claim Rule. #> [DscProperty()] [string] $Description <# .DESCRIPTION Vendor string to set when adding the SATP Claim Rule. #> [DscProperty()] [string] $Vendor <# .DESCRIPTION System default rule added at boot time. #> [DscProperty()] [nullable[bool]] $Boot <# .DESCRIPTION Claim type for the SATP Claim Rule. #> [DscProperty()] [string] $Type <# .DESCRIPTION Device of the SATP Claim Rule. #> [DscProperty()] [string] $Device <# .DESCRIPTION Driver string for the SATP Claim Rule. #> [DscProperty()] [string] $Driver <# .DESCRIPTION Claim option string for the SATP Claim Rule. #> [DscProperty()] [string] $ClaimOptions <# .DESCRIPTION Default PSP for the SATP Claim Rule. #> [DscProperty()] [string] $Psp <# .DESCRIPTION Option string for the SATP Claim Rule. #> [DscProperty()] [string] $Options <# .DESCRIPTION Model string for the SATP Claim Rule. #> [DscProperty()] [string] $Model <# .DESCRIPTION Value, which ignores validity checks and install the rule anyway. #> [DscProperty()] [nullable[bool]] $Force [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $satpClaimRule = $this.GetSatpClaimRule() $satpClaimRulePresent = ($null -ne $satpClaimRule) if ($this.Ensure -eq [Ensure]::Present) { if (!$satpClaimRulePresent) { $this.AddSatpClaimRule() } } else { if ($satpClaimRulePresent) { $this.RemoveSatpClaimRule() } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $satpClaimRule = $this.GetSatpClaimRule() $satpClaimRulePresent = ($null -ne $satpClaimRule) $result = $null if ($this.Ensure -eq [Ensure]::Present) { $result = $satpClaimRulePresent } else { $result = -not $satpClaimRulePresent } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostSatpClaimRule] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostSatpClaimRule]::new() $result.Server = $this.Server $result.RuleName = $this.RuleName $result.Boot = $this.Boot $result.Type = $this.Type $result.Force = $this.Force $vmHost = $this.GetVMHost() $result.Name = $vmHost.Name $this.GetEsxCli($vmHost) $satpClaimRule = $this.GetSatpClaimRule() $satpClaimRulePresent = ($null -ne $satpClaimRule) if (!$satpClaimRulePresent) { $result.Ensure = "Absent" $result.Psp = $this.Psp $this.PopulateResult($result, $this) } else { $result.Ensure = "Present" $result.Psp = $satpClaimRule.DefaultPSP $this.PopulateResult($result, $satpClaimRule) } return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Returns a boolean value indicating if the desired SATP Claim Rule is equal to the passed SATP Claim Rule. #> [bool] Equals($satpClaimRule) { if ($this.RuleName -ne $satpClaimRule.Name) { return $false } <# For every optional property we check if it is not passed(if it is null), because all properties on the server, which are not set are returned as empty strings and if we compare null with empty string the equality will fail. #> if ($null -ne $this.PSPOptions -and $this.PSPOptions -ne $satpClaimRule.PSPOptions) { return $false } if ($null -ne $this.Transport -and $this.Transport -ne $satpClaimRule.Transport) { return $false } if ($null -ne $this.Description -and $this.Description -ne $satpClaimRule.Description) { return $false } if ($null -ne $this.Vendor -and $this.Vendor -ne $satpClaimRule.Vendor) { return $false } if ($null -ne $this.Device -and $this.Device -ne $satpClaimRule.Device) { return $false } if ($null -ne $this.Driver -and $this.Driver -ne $satpClaimRule.Driver) { return $false } if ($null -ne $this.ClaimOptions -and $this.ClaimOptions -ne $satpClaimRule.ClaimOptions) { return $false } if ($null -ne $this.Psp -and $this.Psp -ne $satpClaimRule.DefaultPSP) { return $false } if ($null -ne $this.Options -and $this.Options -ne $satpClaimRule.Options) { return $false } if ($null -ne $this.Model -and $this.Model -ne $satpClaimRule.Model) { return $false } return $true } <# .DESCRIPTION Returns the desired SatpClaimRule if the Rule is present on the server, otherwise returns $null. #> [PSObject] GetSatpClaimRule() { $foundSatpClaimRule = $null $satpClaimRules = Get-SATPClaimRules -EsxCli $this.EsxCli foreach ($satpClaimRule in $satpClaimRules) { if ($this.Equals($satpClaimRule)) { $foundSatpClaimRule = $satpClaimRule break } } return $foundSatpClaimRule } <# .DESCRIPTION Populates the result returned from the Get() method with the SATP Claim Rule properties from the server. #> [void] PopulateResult($result, $satpClaimRule) { $result.PSPoptions = $satpClaimRule.PSPOptions $result.Transport = $satpClaimRule.Transport $result.Description = $satpClaimRule.Description $result.Vendor = $satpClaimRule.Vendor $result.Device = $satpClaimRule.Device $result.Driver = $satpClaimRule.Driver $result.ClaimOptions = $satpClaimRule.ClaimOptions $result.Options = $satpClaimRule.Options $result.Model = $satpClaimRule.Model } <# .DESCRIPTION Populates the arguments for the Add and Remove operations of SATP Claim Rule with the specified properties from the user. #> [void] PopulateSatpArgs($satpArgs) { $satpArgs.satp = $this.RuleName $satpArgs.pspoption = $this.PSPoptions $satpArgs.transport = $this.Transport $satpArgs.description = $this.Description $satpArgs.vendor = $this.Vendor $satpArgs.type = $this.Type $satpArgs.device = $this.Device $satpArgs.driver = $this.Driver $satpArgs.claimoption = $this.ClaimOptions $satpArgs.psp = $this.Psp $satpArgs.option = $this.Options $satpArgs.model = $this.Model if ($null -ne $this.Boot) { $satpArgs.boot = $this.Boot } } <# .DESCRIPTION Installs the new SATP Claim Rule with the specified properties from the user. #> [void] AddSatpClaimRule() { $satpArgs = Add-CreateArgs -EsxCli $this.EsxCli if ($null -ne $this.Force) { $satpArgs.force = $this.Force } $this.PopulateSatpArgs($satpArgs) try { Add-SATPClaimRule -EsxCli $this.EsxCli -SatpArgs $satpArgs } catch { throw "EsxCLI command for adding satp rule failed with the following exception: $($_.Exception.Message)" } } <# .DESCRIPTION Uninstalls the SATP Claim Rule with the specified properties from the user. #> [void] RemoveSatpClaimRule() { $satpArgs = Remove-CreateArgs -EsxCli $this.EsxCli $this.PopulateSatpArgs($satpArgs) try { Remove-SATPClaimRule -EsxCli $this.EsxCli -SatpArgs $satpArgs } catch { throw "EsxCLI command for removing satp rule failed with the following exception: $($_.Exception.Message)" } } } [DscResource()] class VMHostScsiLun : VMHostEntityBaseDSC { <# .DESCRIPTION Specifies the canonical name of the SCSI device. An example of a SCSI canonical name is 'vmhba0:0:0:0'. #> [DscProperty(Key)] [string] $CanonicalName <# .DESCRIPTION Specifies the policy that the Lun must use when choosing a path. The following values are valid: Fixed - uses the preferred SCSI Lun path whenever possible. RoundRobin - load balance. MostRecentlyUsed - uses the most recently used SCSI Lun path. Unknown #> [DscProperty()] [MultipathPolicy] $MultipathPolicy = [MultipathPolicy]::Unset <# .DESCRIPTION Specifies the name of the preferred SCSI Lun path to access the SCSI Lun. #> [DscProperty()] [string] $PreferredScsiLunPathName <# .DESCRIPTION Specifies the maximum number of I/O blocks to be issued on a given path before the system tries to select a different path. Modifying this setting affects all SCSI Lun devices that are connected to the same VMHost. The default value is 2048. Setting this parameter to zero (0) disables switching based on blocks. #> [DscProperty()] [nullable[int]] $BlocksToSwitchPath <# .DESCRIPTION Specifies the maximum number of I/O requests to be issued on a given path before the system tries to select a different path. Modifying this setting affects all SCSI Lun devices that are connected to the same VMHost. The default value is 50. Setting this parameter to zero (0) disables switching based on commands. This parameter is not supported on vCenter Server 4.x. #> [DscProperty()] [nullable[int]] $CommandsToSwitchPath <# .DESCRIPTION Specifies whether to remove all partitions from the SCSI disk. #> [DscProperty()] [nullable[bool]] $DeletePartitions <# .DESCRIPTION Marks the SCSI disk as local or remote. If the value is $true, the SCSI disk is local. If the value is $false, the SCSI disk is remote. #> [DscProperty()] [nullable[bool]] $IsLocal <# .DESCRIPTION Marks the SCSI disk as an SSD or HDD. If the value is $true, the SCSI disk is SSD type. If the value is $false, the SCSI disk is HDD type. #> [DscProperty()] [nullable[bool]] $IsSsd hidden [string] $ModifyScsiLunConfigurationMessage = "Modifying the configuration of SCSI device {0} from VMHost {1}." hidden [string] $CouldNotRetrieveScsiLunMessage = "Could not retrieve SCSI device {0} from VMHost {1}. For more information: {2}" hidden [string] $CouldNotRetrieveScsiLunPathMessage = "Could not retrieve SCSI Lun path {0} to SCSI device {1} from VMHost {2}. For more information: {3}" hidden [string] $CouldNotRetrievePreferredScsiLunPathMessage = "Could not retrieve the preferred SCSI Lun path to SCSI device {0} from VMHost {1}. For more information: {2}" hidden [string] $CouldNotRetrieveScsiLunDiskInformationMessage = "Could not retrieve SCSI Lun disk information for SCSI device {0} from VMHost {1}. For more information: {2}" hidden [string] $CouldNotModifyScsiLunConfigurationMessage = "Could not modify the configuration of SCSI device {0} from VMHost {1}. For more information: {2}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $scsiLun = $this.GetScsiLun() $this.ModifyScsiLunConfiguration($scsiLun) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $scsiLun = $this.GetScsiLun() $result = !$this.ShouldModifyScsiLunConfiguration($scsiLun) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostScsiLun] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostScsiLun]::new() $this.RetrieveVMHost() $scsiLun = $this.GetScsiLun() $this.PopulateResult($result, $scsiLun) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the SCSI device with the specified canonical name from the specified VMHost if it exists. #> [PSObject] GetScsiLun() { try { $scsiLun = Get-ScsiLun -Server $this.Connection -VmHost $this.VMHost -CanonicalName $this.CanonicalName -ErrorAction Stop -Verbose:$false return $scsiLun } catch { throw ($this.CouldNotRetrieveScsiLunMessage -f $this.CanonicalName, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the SCSI Lun path with the specified name to the specified SCSI device if it exists. #> [PSObject] GetScsiLunPath($scsiLun) { try { $scsiLunPath = Get-ScsiLunPath -Name $this.PreferredScsiLunPathName -ScsiLun $scsiLun -ErrorAction Stop -Verbose:$false return $scsiLunPath } catch { throw ($this.CouldNotRetrieveScsiLunPathMessage -f $this.PreferredScsiLunPathName, $scsiLun.CanonicalName, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the preferred SCSI Lun path to the specified SCSI device. #> [PSObject] GetPreferredScsiLunPath($scsiLun) { try { # For each SCSI device there is only one SCSI Lun path which is preferred. $preferredScsiLunPath = Get-ScsiLunPath -ScsiLun $scsiLun -ErrorAction Stop -Verbose:$false | Where-Object -FilterScript { $_.Preferred } return $preferredScsiLunPath } catch { throw ($this.CouldNotRetrievePreferredScsiLunPathMessage -f $scsiLun.CanonicalName, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves SCSI Lun disk information for the specified SCSI device. #> [PSObject] GetVMHostDisk($scsiLun) { try { $vmHostDisk = Get-VMHostDisk -ScsiLun $scsiLun -ErrorAction Stop -Verbose:$false return $vmHostDisk } catch { throw ($this.CouldNotRetrieveScsiLunDiskInformationMessage -f $scsiLun.CanonicalName, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Checks if the SCSI device configuration should be modified. #> [bool] ShouldModifyScsiLunConfiguration($scsiLun) { $shouldModifyScsiLunConfiguration = @( $this.ShouldUpdateDscResourceSetting('MultipathPolicy', [string] $scsiLun.MultipathPolicy, $this.MultipathPolicy.ToString()), $this.ShouldUpdateDscResourceSetting('IsLocal', $scsiLun.IsLocal, $this.IsLocal), $this.ShouldUpdateDscResourceSetting('IsSsd', $scsiLun.IsSsd, $this.IsSsd) ) # 'BlocksToSwitchPath' and 'CommandsToSwitchPath' properties should determine the Desired State only when the desired Multipath policy is 'Round Robin'. if ($this.MultipathPolicy -eq [MultipathPolicy]::RoundRobin) { $shouldModifyScsiLunConfiguration += $this.ShouldUpdateDscResourceSetting( 'BlocksToSwitchPath', [int] $scsiLun.BlocksToSwitchPath, $this.BlocksToSwitchPath ) $shouldModifyScsiLunConfiguration += $this.ShouldUpdateDscResourceSetting( 'CommandsToSwitchPath', [int] $scsiLun.CommandsToSwitchPath, $this.CommandsToSwitchPath ) } if (![string]::IsNullOrEmpty($this.PreferredScsiLunPathName) -and $this.MultipathPolicy -eq [MultipathPolicy]::Fixed) { $scsiLunPath = $this.GetScsiLunPath($scsiLun) # If the found SCSI Lun path is not preferred, the SCSI device is not in a desired state. $shouldModifyScsiLunConfiguration += !$scsiLunPath.Preferred } # If the VMHost disk has existing partitions and 'DeletePartitions' is specified, the SCSI device is not in a desired state. if ($null -ne $this.DeletePartitions -and $this.DeletePartitions) { $vmHostDisk = $this.GetVMHostDisk($scsiLun) $shouldModifyScsiLunConfiguration += ($null -ne $vmHostDisk.ExtensionData.Spec.Partition) } return ($shouldModifyScsiLunConfiguration -Contains $true) } <# .DESCRIPTION Modifies the configuration of the specified SCSI device. #> [void] ModifyScsiLunConfiguration($scsiLun) { $setScsiLunParams = @{ ScsiLun = $scsiLun Confirm = $false ErrorAction = 'Stop' Verbose = $false } if ($this.MultipathPolicy -ne [MultipathPolicy]::Unset) { $setScsiLunParams.MultipathPolicy = $this.MultipathPolicy.ToString() } if ($null -ne $this.IsLocal) { $setScsiLunParams.IsLocal = $this.IsLocal } if ($null -ne $this.IsSsd) { $setScsiLunParams.IsSsd = $this.IsSsd } # 'BlocksToSwitchPath' and 'CommandsToSwitchPath' parameters should be passed to the cmdlet only if the desired Multipath policy is 'Round Robin'. if ($this.MultipathPolicy -eq [MultipathPolicy]::RoundRobin) { if ($null -ne $this.BlocksToSwitchPath) { $setScsiLunParams.BlocksToSwitchPath = $this.BlocksToSwitchPath } if ($null -ne $this.CommandsToSwitchPath) { $setScsiLunParams.CommandsToSwitchPath = $this.CommandsToSwitchPath } } if ($null -ne $this.DeletePartitions) { # Force should be specified to avoid the user being asked for confirmation when deleting disk partitions. $setScsiLunParams.DeletePartitions = $this.DeletePartitions $setScsiLunParams.Force = $true } if (![string]::IsNullOrEmpty($this.PreferredScsiLunPathName) -and $this.MultipathPolicy -eq [MultipathPolicy]::Fixed) { $scsiLunPath = $this.GetScsiLunPath($scsiLun) $setScsiLunParams.PreferredPath = $scsiLunPath } try { $this.WriteLogUtil('Verbose', $this.ModifyScsiLunConfigurationMessage, @($scsiLun.CanonicalName, $this.VMHost.Name)) Set-ScsiLun @setScsiLunParams } catch { throw ($this.CouldNotModifyScsiLunConfigurationMessage -f $scsiLun.CanonicalName, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $scsiLun) { $result.Server = $this.Connection.Name $result.VMHostName = $this.VMHost.Name $result.CanonicalName = $scsiLun.CanonicalName $result.MultipathPolicy = $scsiLun.MultipathPolicy.ToString() $result.BlocksToSwitchPath = [int] $scsiLun.BlocksToSwitchPath $result.CommandsToSwitchPath = [int] $scsiLun.CommandsToSwitchPath $result.IsLocal = $scsiLun.IsLocal $result.IsSsd = $scsiLun.IsSsd $preferredScsiLunPath = $this.GetPreferredScsiLunPath($scsiLun) $result.PreferredScsiLunPathName = $preferredScsiLunPath.Name $result.DeletePartitions = $this.DeletePartitions } } [DscResource()] class VMHostScsiLunPath : VMHostEntityBaseDSC { <# .DESCRIPTION Specifies the name of the SCSI Lun path to the specified SCSI device in 'ScsiLunCanonicalName' key property. #> [DscProperty(Key)] [string] $Name <# .DESCRIPTION Specifies the canonical name of the SCSI device for the specified SCSI Lun path in 'Name' key property. #> [DscProperty(Key)] [string] $ScsiLunCanonicalName <# .DESCRIPTION Specifies whether the SCSI Lun path should be active. #> [DscProperty()] [nullable[bool]] $Active <# .DESCRIPTION Specifies whether the SCSI Lun path should be preferred. Only one SCSI Lun path can be preferred, so when a SCSI Lun path is made preferred, the preference is removed from the previously preferred SCSI Lun path. #> [DscProperty()] [nullable[bool]] $Preferred hidden [string] $ActiveScsiLunPathState = 'Active' hidden [string] $ConfigureScsiLunPathMessage = "Configuring SCSI Lun path {0} to SCSI device {1} from VMHost {2}." hidden [string] $CouldNotRetrieveScsiLunMessage = "Could not retrieve SCSI device {0} from VMHost {1}. For more information: {2}" hidden [string] $CouldNotRetrieveScsiLunPathMessage = "Could not retrieve SCSI Lun path {0} to SCSI device {1} from VMHost {2}. For more information: {3}" hidden [string] $CouldNotConfigureScsiLunPathMessage = "Could not configure SCSI Lun path {0} to SCSI device {1} from VMHost {2}. For more information: {3}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $scsiLun = $this.GetScsiLun() $scsiLunPath = $this.GetScsiLunPath($scsiLun) $this.ConfigureScsiLunPath($scsiLunPath) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $scsiLun = $this.GetScsiLun() $scsiLunPath = $this.GetScsiLunPath($scsiLun) $result = !$this.ShouldConfigureScsiLunPath($scsiLunPath) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostScsiLunPath] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostScsiLunPath]::new() $this.RetrieveVMHost() $scsiLun = $this.GetScsiLun() $scsiLunPath = $this.GetScsiLunPath($scsiLun) $this.PopulateResult($result, $scsiLunPath) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the SCSI device with the specified canonical name from the specified VMHost if it exists. #> [PSObject] GetScsiLun() { try { $scsiLun = Get-ScsiLun -Server $this.Connection -VmHost $this.VMHost -CanonicalName $this.ScsiLunCanonicalName -ErrorAction Stop -Verbose:$false return $scsiLun } catch { throw ($this.CouldNotRetrieveScsiLunMessage -f $this.ScsiLunCanonicalName, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Retrieves the SCSI Lun path with the specified name to the specified SCSI device if it exists. #> [PSObject] GetScsiLunPath($scsiLun) { try { $scsiLunPath = Get-ScsiLunPath -Name $this.Name -ScsiLun $scsiLun -ErrorAction Stop -Verbose:$false return $scsiLunPath } catch { throw ($this.CouldNotRetrieveScsiLunPathMessage -f $this.Name, $scsiLun.CanonicalName, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Checks if the SCSI Lun path should be configured depending on the desired 'Active' and 'Preferred' values. #> [bool] ShouldConfigureScsiLunPath($scsiLunPath) { $shouldConfigureScsiLunPath = @( $this.ShouldUpdateDscResourceSetting('Preferred', $scsiLunPath.Preferred, $this.Preferred) ) if ($null -ne $this.Active) { $currentScsiLunPathState = $scsiLunPath.State.ToString() if ($this.Active) { $shouldConfigureScsiLunPath += ($currentScsiLunPathState -ne $this.ActiveScsiLunPathState) } else { $shouldConfigureScsiLunPath += ($currentScsiLunPathState -eq $this.ActiveScsiLunPathState) } } return ($shouldConfigureScsiLunPath -Contains $true) } <# .DESCRIPTION Configures the SCSI Lun path with the specified name with the desired 'Active' and 'Preferred' values. #> [void] ConfigureScsiLunPath($scsiLunPath) { $setScsiLunPathParams = @{ ScsiLunPath = $scsiLunPath Confirm = $false ErrorAction = 'Stop' Verbose = $false } if ($null -ne $this.Active) { $setScsiLunPathParams.Active = $this.Active } if ($null -ne $this.Preferred) { $setScsiLunPathParams.Preferred = $this.Preferred } try { $this.WriteLogUtil('Verbose', $this.ConfigureScsiLunPathMessage, @($scsiLunPath.Name, $scsiLunPath.ScsiLun.CanonicalName, $this.VMHost.Name)) Set-ScsiLunPath @setScsiLunPathParams } catch { throw ($this.CouldNotConfigureScsiLunPathMessage -f $scsiLunPath.Name, $scsiLunPath.ScsiLun.CanonicalName, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $scsiLunPath) { $result.Server = $this.Connection.Name $result.VMHostName = $this.VMHost.Name $result.Name = $scsiLunPath.Name $result.ScsiLunCanonicalName = $scsiLunPath.ScsiLun.CanonicalName $result.Active = if ($scsiLunPath.State.ToString() -eq $this.ActiveScsiLunPathState) { $true } else { $false } $result.Preferred = $scsiLunPath.Preferred } } [DscResource()] class VMHostService : VMHostBaseDSC { <# .DESCRIPTION The key value of the service. #> [DscProperty(Key)] [string] $Key <# .DESCRIPTION The state of the service after a VMHost reboot. #> [DscProperty()] [ServicePolicy] $Policy = [ServicePolicy]::Unset <# .DESCRIPTION The current state of the service. #> [DscProperty()] [nullable[bool]] $Running <# .DESCRIPTION Host Service Label. #> [DscProperty(NotConfigurable)] [string] $Label <# .DESCRIPTION Host Service Required flag. #> [DscProperty(NotConfigurable)] [bool] $Required <# .DESCRIPTION Firewall rules for the service. #> [DscProperty(NotConfigurable)] [string[]] $Ruleset [void] Set() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.UpdateVMHostService($vmHost) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $result = !$this.ShouldUpdateVMHostService($vmHost) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostService] Get() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $result = [VMHostService]::new() $result.Server = $this.Server $vmHost = $this.GetVMHost() $this.PopulateResult($vmHost, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Returns a boolean value indicating if the VMHostService should to be updated. #> [bool] ShouldUpdateVMHostService($vmHost) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vmHostCurrentService = Get-VMHostService -Server $this.Connection -VMHost $vmHost | Where-Object { $_.Key -eq $this.Key } $shouldUpdateVMHostService = @( $this.ShouldUpdateDscResourceSetting('Policy', [string] $vmHostCurrentService.Policy, $this.Policy.ToString()), $this.ShouldUpdateDscResourceSetting('Running', $vmHostCurrentService.Running, $this.Running) ) return ($shouldUpdateVMHostService -Contains $true) } <# .DESCRIPTION Updates the configuration of the VMHostService. #> [void] UpdateVMHostService($vmHost) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vmHostCurrentService = Get-VMHostService -Server $this.Connection -VMHost $vmHost | Where-Object { $_.Key -eq $this.Key } if ($this.ShouldUpdateDscResourceSetting('Policy', [string] $vmHostCurrentService.Policy, $this.Policy.ToString())) { Set-VMHostService -HostService $vmHostCurrentService -Policy $this.Policy.ToString() -Confirm:$false } if ($this.ShouldUpdateDscResourceSetting('Running', $vmHostCurrentService.Running, $this.Running)) { if ($vmHostCurrentService.Running) { Stop-VMHostService -HostService $vmHostCurrentService -Confirm:$false } else { Start-VMHostService -HostService $vmHostCurrentService -Confirm:$false } } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the VMHostService from the server. #> [void] PopulateResult($vmHost, $vmHostService) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vmHostCurrentService = Get-VMHostService -Server $this.Connection -VMHost $vmHost | Where-Object { $_.Key -eq $this.Key } $vmHostService.Name = $vmHost.Name $vmHostService.Server = $this.Server $vmHostService.Key = $vmHostCurrentService.Key $vmHostService.Policy = $vmHostCurrentService.Policy $vmHostService.Running = $vmHostCurrentService.Running $vmHostService.Label = $vmHostCurrentService.Label $vmHostService.Required = $vmHostCurrentService.Required $vmHostService.Ruleset = $vmHostCurrentService.Ruleset } } [DscResource()] class VMHostSettings : VMHostBaseDSC { <# .DESCRIPTION Motd Advanced Setting value. #> [DscProperty()] [string] $Motd <# .DESCRIPTION Indicates whether the Motd content should be cleared. #> [DscProperty()] [bool] $MotdClear <# .DESCRIPTION Issue Advanced Setting value. #> [DscProperty()] [string] $Issue <# .DESCRIPTION Indicates whether the Issue content should be cleared. #> [DscProperty()] [bool] $IssueClear hidden [string] $IssueSettingName = "Config.Etc.issue" hidden [string] $MotdSettingName = "Config.Etc.motd" [void] Set() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.UpdateVMHostSettings($vmHost) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $result = !$this.ShouldUpdateVMHostSettings($vmHost) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostSettings] Get() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $result = [VMHostSettings]::new() $result.Server = $this.Server $vmHost = $this.GetVMHost() $this.PopulateResult($vmHost, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Returns a boolean value indicating if at least one Advanced Setting value should be updated. #> [bool] ShouldUpdateVMHostSettings($vmHost) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vmHostCurrentAdvancedSettings = Get-AdvancedSetting -Server $this.Connection -Entity $vmHost $currentMotd = $vmHostCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.MotdSettingName } $currentIssue = $vmHostCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.IssueSettingName } $motdDesiredValue = if ($this.MotdClear) { [string]::Empty } else { $this.Motd } $issueDesiredValue = if ($this.IssueClear) { [string]::Empty } else { $this.Issue } $shouldUpdateVMHostSettings = @( $this.ShouldUpdateDscResourceSetting('Motd', $currentMotd.Value, $motdDesiredValue), $this.ShouldUpdateDscResourceSetting('Issue', $currentIssue.Value, $issueDesiredValue) ) return ($shouldUpdateVMHostSettings -Contains $true) } <# .DESCRIPTION Sets the desired value for the Advanced Setting, if update of the Advanced Setting value is needed. #> [void] SetAdvancedSetting($advancedSettingName, $advancedSetting, $advancedSettingDesiredValue, $advancedSettingCurrentValue, $clearValue) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) if ($clearValue) { if ($this.ShouldUpdateDscResourceSetting($advancedSettingName, $advancedSettingCurrentValue, [string]::Empty)) { Set-AdvancedSetting -AdvancedSetting $advancedSetting -Value [string]::Empty -Confirm:$false } } else { if ($this.ShouldUpdateDscResourceSetting($advancedSettingName, $advancedSettingCurrentValue, $advancedSettingDesiredValue)) { Set-AdvancedSetting -AdvancedSetting $advancedSetting -Value $advancedSettingDesiredValue -Confirm:$false } } } <# .DESCRIPTION Performs update on those Advanced Settings values that needs to be updated. #> [void] UpdateVMHostSettings($vmHost) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vmHostCurrentAdvancedSettings = Get-AdvancedSetting -Server $this.Connection -Entity $vmHost $currentMotd = $vmHostCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.MotdSettingName } $currentIssue = $vmHostCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.IssueSettingName } $this.SetAdvancedSetting('Motd', $currentMotd, $this.Motd, $currentMotd.Value, $this.MotdClear) $this.SetAdvancedSetting('Issue', $currentIssue, $this.Issue, $currentIssue.Value, $this.IssueClear) } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the advanced settings from the server. #> [void] PopulateResult($vmHost, $result) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vmHostCurrentAdvancedSettings = Get-AdvancedSetting -Server $this.Connection -Entity $vmHost $currentMotd = $vmHostCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.MotdSettingName } $currentIssue = $vmHostCurrentAdvancedSettings | Where-Object { $_.Name -eq $this.IssueSettingName } $result.Name = $vmHost.Name $result.Motd = $currentMotd.Value $result.Issue = $currentIssue.Value } } [DscResource()] class VMHostStorage : VMHostBaseDSC { <# .DESCRIPTION Specifies whether the software iSCSI is enabled on the VMHost storage. #> [DscProperty(Mandatory)] [bool] $Enabled hidden [string] $RetrieveVMHostStorageMessage = "Retrieving VMHost storage for VMHost {0}." hidden [string] $ConfigureVMHostStorageMessage = "Configuring VMHost storage for VMHost {0}." hidden [string] $CouldNotRetrieveVMHostStorageMessage = "Could not retrieve VMHost storage for VMHost {0}. For more information: {1}" hidden [string] $CouldNotConfigureVMHostStorageMessage = "Could not configure VMHost storage for VMHost {0}. For more information: {1}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostStorage = $this.GetVMHostStorage($vmHost) $this.ConfigureVMHostStorage($vmHostStorage) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostStorage = $this.GetVMHostStorage($vmHost) $result = !$this.ShouldConfigureVMHostStorage($vmHostStorage) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostStorage] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $result = [VMHostStorage]::new() $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostStorage = $this.GetVMHostStorage($vmHost) $this.PopulateResult($result, $vmHostStorage) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the VMHost storage for the specified VMHost. #> [PSObject] GetVMHostStorage($vmHost) { $getVMHostStorageParams = @{ Server = $this.Connection VMHost = $vmHost ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.RetrieveVMHostStorageMessage, @($vmHost.Name)) return Get-VMHostStorage @getVMHostStorageParams } catch { throw ($this.CouldNotRetrieveVMHostStorageMessage -f $vmHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Checks if the specified VMHost storage should be configured - whether to enable or disable the software iSCSI support. #> [bool] ShouldConfigureVMHostStorage($vmHostStorage) { return $this.ShouldUpdateDscResourceSetting('Enabled', $vmHostStorage.SoftwareIScsiEnabled, $this.Enabled) } <# .DESCRIPTION Configures the VMHost storage - enables or disables the software iSCSI support. #> [void] ConfigureVMHostStorage($vmHostStorage) { $setVMHostStorageParams = @{ VMHostStorage = $vmHostStorage SoftwareIScsiEnabled = $this.Enabled Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.ConfigureVMHostStorageMessage, @($vmHostStorage.VMHost.Name)) Set-VMHostStorage @setVMHostStorageParams } catch { throw ($this.CouldNotConfigureVMHostStorageMessage -f $vmHostStorage.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHostStorage) { $result.Server = $this.Connection.Name $result.Name = $vmHostStorage.VMHost.Name $result.Enabled = $vmHostStorage.SoftwareIScsiEnabled } } [DscResource()] class VMHostSyslog : VMHostBaseDSC { <# .DESCRIPTION The remote host(s) to send logs to. #> [DscProperty()] [string] $Loghost <# .DESCRIPTION Verify remote SSL certificates against the local CA Store. #> [DscProperty()] [nullable[bool]] $CheckSslCerts <# .DESCRIPTION Default network retry timeout in seconds if a remote server fails to respond. #> [DscProperty()] [nullable[long]] $DefaultTimeout <# .DESCRIPTION Message queue capacity after which messages are dropped. #> [DscProperty()] [nullable[long]] $QueueDropMark <# .DESCRIPTION The directory to output local logs to. #> [DscProperty()] [string] $Logdir <# .DESCRIPTION Place logs in a unique subdirectory of logdir, based on hostname. #> [DscProperty()] [nullable[bool]] $LogdirUnique <# .DESCRIPTION Default number of rotated local logs to keep. #> [DscProperty()] [nullable[long]] $DefaultRotate <# .DESCRIPTION Default size of local logs before rotation, in KiB. #> [DscProperty()] [nullable[long]] $DefaultSize <# .DESCRIPTION Number of rotated dropped log files to keep. #> [DscProperty()] [nullable[long]] $DropLogRotate <# .DESCRIPTION Size of dropped log file before rotation, in KiB. #> [DscProperty()] [nullable[long]] $DropLogSize [void] Set() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.UpdateVMHostSyslog($vmHost) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $result = !$this.ShouldUpdateVMHostSyslog($vmHost) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostSyslog] Get() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $result = [VMHostSyslog]::new() $vmHost = $this.GetVMHost() $this.PopulateResult($vmHost, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Returns a boolean value indicating if VMHostSyslog needs to be updated. #> [bool] ShouldUpdateVMHostSyslog($VMHost) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $esxcli = Get-Esxcli -Server $this.Connection -VMHost $vmHost -V2 $current = Get-VMHostSyslogConfig -EsxCLi $esxcli $shouldUpdateVMHostSyslog = @( $this.ShouldUpdateDscResourceSetting('LogHost', [string] $current.RemoteHost, $this.LogHost), $this.ShouldUpdateDscResourceSetting('CheckSslCerts', $current.EnforceSSLCertificates, $this.CheckSslCerts), $this.ShouldUpdateDscResourceSetting('DefaultTimeout', $current.DefaultNetworkRetryTimeout, $this.DefaultTimeout), $this.ShouldUpdateDscResourceSetting('QueueDropMark', $current.MessageQueueDropMark, $this.QueueDropMark), $this.ShouldUpdateDscResourceSetting('Logdir', [string] $current.LocalLogOutput, $this.Logdir), $this.ShouldUpdateDscResourceSetting('LogdirUnique', [System.Convert]::ToBoolean($current.LogToUniqueSubdirectory), $this.LogdirUnique), $this.ShouldUpdateDscResourceSetting('DefaultRotate', $current.LocalLoggingDefaultRotations, $this.DefaultRotate), $this.ShouldUpdateDscResourceSetting('DefaultSize', $current.LocalLoggingDefaultRotationSize, $this.DefaultSize), $this.ShouldUpdateDscResourceSetting('DropLogRotate', $current.DroppedLogFileRotations, $this.DropLogRotate), $this.ShouldUpdateDscResourceSetting('DropLogSize', $current.DroppedLogFileRotationSize, $this.DropLogSize) ) return ($shouldUpdateVMHostSyslog -contains $true) } <# .DESCRIPTION Updates the configuration of the VMHostSyslog. #> [void] UpdateVMHostSyslog($VMHost) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $esxcli = Get-Esxcli -Server $this.Connection -VMHost $vmHost -V2 $vmHostSyslogConfig = @{ queuedropmark = $this.QueueDropMark defaultrotate = $this.DefaultRotate droplogrotate = $this.DropLogRotate } if ($null -ne $this.CheckSslCerts) { $vmHostSyslogConfig.checksslcerts = $this.CheckSslCerts } if ($null -ne $this.DefaultTimeout) { $vmHostSyslogConfig.defaulttimeout = $this.DefaultTimeout } if (![string]::IsNullOrEmpty($this.Logdir)) { $vmHostSyslogConfig.logdir = $this.Logdir } if ($null -ne $this.LogdirUnique) { $vmHostSyslogConfig.logdirunique = $this.LogdirUnique } if ($null -ne $this.DefaultSize) { $vmHostSyslogConfig.defaultsize = $this.DefaultSize } if ($null -ne $this.DropLogSize) { $vmHostSyslogConfig.droplogsize = $this.DropLogSize } if (![string]::IsNullOrEmpty($this.LogHost)) { $vmHostSyslogConfig.loghost = $this.Loghost } Set-VMHostSyslogConfig -EsxCli $esxcli -VMHostSyslogConfig $vmHostSyslogConfig } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the VMHostService from the server. #> [void] PopulateResult($VMHost, $syslog) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $esxcli = Get-Esxcli -Server $this.Connection -VMHost $vmHost -V2 $currentVMHostSyslog = Get-VMHostSyslogConfig -EsxCLi $esxcli $syslog.Server = $this.Server $syslog.Name = $VMHost.Name $syslog.Loghost = $currentVMHostSyslog.RemoteHost $syslog.CheckSslCerts = [System.Convert]::ToBoolean($currentVMHostSyslog.EnforceSSLCertificates) $syslog.DefaultTimeout = [long] $currentVMHostSyslog.DefaultNetworkRetryTimeout $syslog.QueueDropMark = [long] $currentVMHostSyslog.MessageQueueDropMark $syslog.Logdir = $currentVMHostSyslog.LocalLogOutput $syslog.LogdirUnique = [System.Convert]::ToBoolean($currentVMHostSyslog.LogToUniqueSubdirectory) $syslog.DefaultRotate = [long] $currentVMHostSyslog.LocalLoggingDefaultRotations $syslog.DefaultSize = [long] $currentVMHostSyslog.LocalLoggingDefaultRotationSize $syslog.DropLogRotate = [long] $currentVMHostSyslog.DroppedLogFileRotations $syslog.DropLogSize = [long] $currentVMHostSyslog.DroppedLogFileRotationSize } } [DscResource()] class VMHostTpsSettings : VMHostBaseDSC { <# .DESCRIPTION Share Scan Time Advanced Setting value. #> [DscProperty()] [nullable[int]] $ShareScanTime <# .DESCRIPTION Share Scan GHz Advanced Setting value. #> [DscProperty()] [nullable[int]] $ShareScanGHz <# .DESCRIPTION Share Rate Max Advanced Setting value. #> [DscProperty()] [nullable[int]] $ShareRateMax <# .DESCRIPTION Share Force Salting Advanced Setting value. #> [DscProperty()] [nullable[int]] $ShareForceSalting hidden [string] $TpsSettingsName = "Mem.Sh*" hidden [string] $MemValue = "Mem." [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.UpdateTpsSettings($vmHost) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $result = !$this.ShouldUpdateTpsSettings($vmHost) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostTpsSettings] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostTpsSettings]::new() $result.Server = $this.Server $vmHost = $this.GetVMHost() $result.Name = $vmHost.Name $tpsSettings = $this.GetTpsAdvancedSettings($vmHost) $vmHostTpsSettingsDscResourcePropertyNames = $this.GetType().GetProperties().Name foreach ($tpsSetting in $tpsSettings) { $tpsSettingName = $tpsSetting.Name.TrimStart($this.MemValue) if ($vmHostTpsSettingsDscResourcePropertyNames -Contains $tpsSettingName) { $result.$tpsSettingName = $tpsSetting.Value } } return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the Tps advanced settings for the specified VMHost from the server. #> [PSObject] GetTpsAdvancedSettings($vmHost) { $getAdvancedSettingParams = @{ Server = $this.Connection Entity = $vmHost Name = $this.TpsSettingsName ErrorAction = 'Stop' Verbose = $false } return Get-AdvancedSetting @getAdvancedSettingParams } <# .DESCRIPTION Returns a boolean value, indicating if update operation should be performed on at least one of the TPS Settings. #> [bool] ShouldUpdateTpsSettings($vmHost) { $tpsSettings = $this.GetTpsAdvancedSettings($vmHost) foreach ($tpsSetting in $tpsSettings) { $tpsSettingName = $tpsSetting.Name.TrimStart($this.MemValue) if ($null -ne $this.$tpsSettingName -and $this.$tpsSettingName -ne $tpsSetting.Value) { $this.WriteLogUtil('Verbose', $this.SettingIsNotInDesiredStateMessage, @($tpsSettingName, $tpsSetting.Value, $this.$tpsSettingName)) return $true } } return $false } <# .DESCRIPTION Updates the needed TPS Settings with the specified values. #> [void] UpdateTpsSettings($vmHost) { $tpsSettings = $this.GetTpsAdvancedSettings($vmHost) foreach ($tpsSetting in $tpsSettings) { $tpsSettingName = $tpsSetting.Name.TrimStart($this.MemValue) if ($null -eq $this.$tpsSettingName -or $this.$tpsSettingName -eq $tpsSetting.Value) { continue } $setAdvancedSettingParams = @{ AdvancedSetting = $tpsSetting Value = $this.$tpsSettingName Confirm = $false ErrorAction = 'Stop' Verbose = $false } Set-AdvancedSetting @setAdvancedSettingParams } } } [DscResource()] class NfsDatastore : DatastoreBaseDSC { <# .DESCRIPTION Specifies the Nfs Host for the Datastore. #> [DscProperty(Mandatory)] [string[]] $NfsHost <# .DESCRIPTION Specifies the access mode for the Nfs Datastore. Valid access modes are 'ReadWrite' and 'ReadOnly'. The default access mode is 'ReadWrite'. #> [DscProperty()] [AccessMode] $AccessMode = [AccessMode]::ReadWrite <# .DESCRIPTION Specifies the authentication method for the Nfs Datastore. Valid authentication methods are 'AUTH_SYS' and 'Kerberos'. The default authentication method is 'AUTH_SYS'. #> [DscProperty()] [AuthenticationMethod] $AuthenticationMethod = [AuthenticationMethod]::AUTH_SYS [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $datastore = $this.GetDatastore() if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $datastore) { $datastore = $this.NewNfsDatastore() } if ($this.ShouldModifyDatastore($datastore)) { $this.ModifyDatastore($datastore) } } else { if ($null -ne $datastore) { $this.RemoveDatastore($datastore) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $datastore = $this.GetDatastore() $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $datastore) { $result = $false } else { $result = !$this.ShouldModifyDatastore($datastore) } } else { $result = ($null -eq $datastore) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [NfsDatastore] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [NfsDatastore]::new() $this.RetrieveVMHost() $datastore = $this.GetDatastore() $this.PopulateResultForNfsDatastore($result, $datastore) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Creates a new Nfs Datastore with the specified name on the VMHost. #> [PSObject] NewNfsDatastore() { $newDatastoreParams = @{ Nfs = $true NfsHost = $this.NfsHost } if ($this.AccessMode -eq [AccessMode]::ReadOnly) { $newDatastoreParams.ReadOnly = $true } if ($this.AuthenticationMethod -eq [AuthenticationMethod]::Kerberos) { $newDatastoreParams.Kerberos = $true } return $this.NewDatastore($newDatastoreParams) } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResultForNfsDatastore($result, $datastore) { if ($null -ne $datastore) { $result.NfsHost = $datastore.RemoteHost $result.Path = $datastore.RemotePath $result.AccessMode = [AccessMode] $datastore.ExtensionData.Host.MountInfo.AccessMode $result.AuthenticationMethod = $datastore.AuthenticationMethod.ToString() } else { $result.NfsHost = $this.NfsHost $result.Path = $this.Path $result.AccessMode = $this.AccessMode $result.AuthenticationMethod = $this.AuthenticationMethod } $this.PopulateResult($result, $datastore) } } [DscResource()] class VmfsDatastore : DatastoreBaseDSC { <# .DESCRIPTION Specifies the maximum file size of Vmfs in megabytes (MB). If no value is specified, the maximum file size for the current system platform is used. #> [DscProperty()] [nullable[int]] $BlockSizeMB hidden [string] $CouldNotCreateVmfsDatastoreWithTheSpecifiedNameMessage = "Could not create Vmfs Datastore {0} on VMHost {1} because there is another Vmfs Datastore with the same name on vCenter Server {2}." [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $datastore = $this.GetDatastore() $this.ValidateVmfsDatastoreCreation($datastore) if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $datastore) { $datastore = $this.NewVmfsDatastore() } if ($this.ShouldModifyDatastore($datastore)) { $this.ModifyDatastore($datastore) } } else { if ($null -ne $datastore) { $this.RemoveDatastore($datastore) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.RetrieveVMHost() $datastore = $this.GetDatastore() $this.ValidateVmfsDatastoreCreation($datastore) $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $datastore) { $result = $false } else { $result = !$this.ShouldModifyDatastore($datastore) } } else { $result = ($null -eq $datastore) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VmfsDatastore] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VmfsDatastore]::new() $this.RetrieveVMHost() $datastore = $this.GetDatastore() $this.ValidateVmfsDatastoreCreation($datastore) $this.PopulateResultForVmfsDatastore($result, $datastore) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Checks if a Vmfs Datastore with the specified name can be created on the vCenter Server. #> [void] ValidateVmfsDatastoreCreation($datastore) { <# If the established connection is to a vCenter Server, Ensure is 'Present' and the Vmfs Datastore does not exist on the specified VMHost, we need to check if there is a Vmfs Datastore with the same name on the vCenter Server. #> if ($this.Connection.ProductLine -eq $this.vCenterProductId -and $this.Ensure -eq [Ensure]::Present -and $null -eq $datastore) { $getDatastoreParams = @{ Server = $this.Connection Name = $this.Name ErrorAction = 'SilentlyContinue' Verbose = $false } <# If there is another Vmfs Datastore with the same name on the vCenter Server but on a different VMHost, we need to inform the user that the Vmfs Datastore cannot be created with the specified name. vCenter Server accepts multiple Vmfs Datastore creations with the same name but changes the names internally to avoid name duplication. vCenter Server appends '(<index>)' to the Vmfs Datastore name. #> $datastoreInvCenter = Get-Datastore @getDatastoreParams if ($null -ne $datastoreInvCenter) { throw ($this.CouldNotCreateVmfsDatastoreWithTheSpecifiedNameMessage -f $this.Name, $this.VMHost.Name, $this.Connection.Name) } } } <# .DESCRIPTION Creates a new Vmfs Datastore with the specified name on the VMHost. #> [PSObject] NewVmfsDatastore() { $newDatastoreParams = @{ Vmfs = $true } if ($null -ne $this.BlockSizeMB) { $newDatastoreParams.BlockSizeMB = $this.BlockSizeMB } return $this.NewDatastore($newDatastoreParams) } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResultForVmfsDatastore($result, $datastore) { if ($null -ne $datastore) { $result.BlockSizeMB = $datastore.ExtensionData.Info.Vmfs.BlockSizeMB $result.Path = $datastore.ExtensionData.Info.Vmfs.Extent | Where-Object -FilterScript { $_.DiskName -eq $this.Path } | Select-Object -ExpandProperty DiskName } else { $result.BlockSizeMB = $this.BlockSizeMB $result.Path = $this.Path } $this.PopulateResult($result, $datastore) } } [DscResource()] class VMHostVssPortGroup : VMHostVssPortGroupBaseDSC { <# .DESCRIPTION Specifies the Name of the Virtual Switch associated with the Port Group. The Virtual Switch must be a Standard Virtual Switch. #> [DscProperty(Mandatory)] [string] $VssName <# .DESCRIPTION Specifies the VLAN ID for ports using this Port Group. The following values are valid: 0 - specifies that you do not want to associate the Port Group with a VLAN. 1 to 4094 - specifies a VLAN ID for the Port Group. 4095 - specifies that the Port Group should use trunk mode, which allows the guest operating system to manage its own VLAN tags. #> [DscProperty()] [nullable[int]] $VLanId hidden [int] $VLanIdMaxValue = 4095 [void] Set() { try { $this.ConnectVIServer() $this.RetrieveVMHost() $virtualSwitch = $this.GetVirtualSwitch() $portGroup = $this.GetVirtualPortGroup($virtualSwitch) if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $portGroup) { $this.AddVirtualPortGroup($virtualSwitch) } else { $this.UpdateVirtualPortGroup($portGroup) } } else { if ($null -ne $portGroup) { $this.RemoveVirtualPortGroup($portGroup) } } } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $this.RetrieveVMHost() $virtualSwitch = $this.GetVirtualSwitch() $portGroup = $this.GetVirtualPortGroup($virtualSwitch) $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $portGroup) { $result = $false } else { $result = !$this.ShouldUpdateDscResourceSetting('VLanId', $portGroup.VLanId, $this.VLanId) } } else { $result = ($null -eq $portGroup) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostVssPortGroup] Get() { try { $result = [VMHostVssPortGroup]::new() $result.Server = $this.Server $this.ConnectVIServer() $this.RetrieveVMHost() $virtualSwitch = $this.GetVirtualSwitch() $portGroup = $this.GetVirtualPortGroup($virtualSwitch) $result.VMHostName = $this.VMHost.Name $this.PopulateResult($portGroup, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the Virtual Switch with the specified name from the server if it exists. The Virtual Switch must be a Standard Virtual Switch. If the Virtual Switch does not exist and Ensure is set to 'Absent', $null is returned. Otherwise it throws an exception. #> [PSObject] GetVirtualSwitch() { if ($this.Ensure -eq [Ensure]::Absent) { return Get-VirtualSwitch -Server $this.Connection -Name $this.VssName -VMHost $this.VMHost -Standard -ErrorAction SilentlyContinue } else { try { $virtualSwitch = Get-VirtualSwitch -Server $this.Connection -Name $this.VssName -VMHost $this.VMHost -Standard -ErrorAction Stop return $virtualSwitch } catch { throw "Could not retrieve Virtual Switch $($this.VssName) of VMHost $($this.VMHost.Name). For more information: $($_.Exception.Message)" } } } <# .DESCRIPTION Retrieves the Virtual Port Group with the specified Name, available on the specified Virtual Switch and VMHost from the server if it exists, otherwise returns $null. #> [PSObject] GetVirtualPortGroup($virtualSwitch) { if ($null -eq $virtualSwitch) { <# If the Virtual Switch is $null, it means that Ensure was set to 'Absent' and the Port Group does not exist for the specified Virtual Switch. #> return $null } return Get-VirtualPortGroup -Server $this.Connection -Name $this.Name -VirtualSwitch $virtualSwitch -VMHost $this.VMHost -ErrorAction SilentlyContinue } <# .DESCRIPTION Ensures that the passed VLanId value is in the range [0, 4095]. #> [void] EnsureVLanIdValueIsValid() { if ($this.VLanId -lt 0 -or $this.VLanId -gt $this.VLanIdMaxValue) { throw "The passed VLanId value $($this.VLanId) is not valid. The valid values are in the following range: [0, $($this.VLanIdMaxValue)]." } } <# .DESCRIPTION Returns the populated Port Group parameters. #> [hashtable] GetPortGroupParams() { $portGroupParams = @{} $portGroupParams.Confirm = $false $portGroupParams.ErrorAction = 'Stop' if ($null -ne $this.VLanId) { $this.EnsureVLanIdValueIsValid() $portGroupParams.VLanId = $this.VLanId } return $portGroupParams } <# .DESCRIPTION Creates a new Port Group available on the specified Virtual Switch. #> [void] AddVirtualPortGroup($virtualSwitch) { $portGroupParams = $this.GetPortGroupParams() $portGroupParams.Server = $this.Connection $portGroupParams.Name = $this.Name $portGroupParams.VirtualSwitch = $virtualSwitch try { New-VirtualPortGroup @portGroupParams } catch { throw "Cannot create Virtual Port Group $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Updates the Port Group by changing its VLanId value. #> [void] UpdateVirtualPortGroup($portGroup) { $portGroupParams = $this.GetPortGroupParams() try { $portGroup | Set-VirtualPortGroup @portGroupParams } catch { throw "Cannot update Virtual Port Group $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Removes the specified Port Group available on the Virtual Switch. All VMs connected to the Port Group must be PoweredOff to successfully remove the Port Group. If one or more of the VMs are PoweredOn, the removal would not be successful because the Port Group is used by the VMs. #> [void] RemoveVirtualPortGroup($portGroup) { try { $portGroup | Remove-VirtualPortGroup -Confirm:$false -ErrorAction Stop } catch { throw "Cannot remove Virtual Port Group $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Port Group from the server. #> [void] PopulateResult($portGroup, $result) { if ($null -ne $portGroup) { $result.Name = $portGroup.Name $result.VssName = $portGroup.VirtualSwitchName $result.Ensure = [Ensure]::Present $result.VLanId = $portGroup.VLanId } else { $result.Name = $this.Name $result.VssName = $this.VssName $result.Ensure = [Ensure]::Absent $result.VLanId = $this.VLanId } } } [DscResource()] class VMHostVssPortGroupSecurity : VMHostVssPortGroupBaseDSC { <# .DESCRIPTION Specifies whether promiscuous mode is enabled for the corresponding Virtual Port Group. #> [DscProperty()] [nullable[bool]] $AllowPromiscuous <# .DESCRIPTION Specifies whether the AllowPromiscuous setting is inherited from the parent Standard Virtual Switch. #> [DscProperty()] [nullable[bool]] $AllowPromiscuousInherited <# .DESCRIPTION Specifies whether forged transmits are enabled for the corresponding Virtual Port Group. #> [DscProperty()] [nullable[bool]] $ForgedTransmits <# .DESCRIPTION Specifies whether the ForgedTransmits setting is inherited from the parent Standard Virtual Switch. #> [DscProperty()] [nullable[bool]] $ForgedTransmitsInherited <# .DESCRIPTION Specifies whether MAC address changes are enabled for the corresponding Virtual Port Group. #> [DscProperty()] [nullable[bool]] $MacChanges <# .DESCRIPTION Specifies whether the MacChanges setting is inherited from the parent Standard Virtual Switch. #> [DscProperty()] [nullable[bool]] $MacChangesInherited hidden [string] $AllowPromiscuousSettingName = 'AllowPromiscuous' hidden [string] $AllowPromiscuousInheritedSettingName = 'AllowPromiscuousInherited' hidden [string] $ForgedTransmitsSettingName = 'ForgedTransmits' hidden [string] $ForgedTransmitsInheritedSettingName = 'ForgedTransmitsInherited' hidden [string] $MacChangesSettingName = 'MacChanges' hidden [string] $MacChangesInheritedSettingName = 'MacChangesInherited' [void] Set() { try { $this.ConnectVIServer() $this.RetrieveVMHost() $virtualPortGroup = $this.GetVirtualPortGroup() $virtualPortGroupSecurityPolicy = $this.GetVirtualPortGroupSecurityPolicy($virtualPortGroup) $this.UpdateVirtualPortGroupSecurityPolicy($virtualPortGroupSecurityPolicy) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $this.RetrieveVMHost() $virtualPortGroup = $this.GetVirtualPortGroup() $result = $null if ($null -eq $virtualPortGroup) { # If the Port Group is $null, it means that Ensure is 'Absent' and the Port Group does not exist. $result = $true } else { $virtualPortGroupSecurityPolicy = $this.GetVirtualPortGroupSecurityPolicy($virtualPortGroup) $result = !$this.ShouldUpdateVirtualPortGroupSecurityPolicy($virtualPortGroupSecurityPolicy) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostVssPortGroupSecurity] Get() { try { $result = [VMHostVssPortGroupSecurity]::new() $result.Server = $this.Server $result.Ensure = $this.Ensure $this.ConnectVIServer() $this.RetrieveVMHost() $result.VMHostName = $this.VMHost.Name $virtualPortGroup = $this.GetVirtualPortGroup() if ($null -eq $virtualPortGroup) { # If the Port Group is $null, it means that Ensure is 'Absent' and the Port Group does not exist. $result.Name = $this.Name return $result } $virtualPortGroupSecurityPolicy = $this.GetVirtualPortGroupSecurityPolicy($virtualPortGroup) $result.Name = $virtualPortGroup.Name $this.PopulateResult($virtualPortGroupSecurityPolicy, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the Virtual Port Group Security Policy from the server. #> [PSObject] GetVirtualPortGroupSecurityPolicy($virtualPortGroup) { try { $virtualPortGroupSecurityPolicy = Get-SecurityPolicy -Server $this.Connection -VirtualPortGroup $virtualPortGroup -ErrorAction Stop return $virtualPortGroupSecurityPolicy } catch { throw "Could not retrieve Virtual Port Group $($this.PortGroup) Security Policy. For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Checks if the Security Policy of the specified Virtual Port Group should be updated. #> [bool] ShouldUpdateVirtualPortGroupSecurityPolicy($virtualPortGroupSecurityPolicy) { $shouldUpdateVirtualPortGroupSecurityPolicy = @( $this.ShouldUpdateDscResourceSetting('AllowPromiscuous', $virtualPortGroupSecurityPolicy.AllowPromiscuous, $this.AllowPromiscuous), $this.ShouldUpdateDscResourceSetting('AllowPromiscuousInherited', $virtualPortGroupSecurityPolicy.AllowPromiscuousInherited, $this.AllowPromiscuousInherited), $this.ShouldUpdateDscResourceSetting('ForgedTransmits', $virtualPortGroupSecurityPolicy.ForgedTransmits, $this.ForgedTransmits), $this.ShouldUpdateDscResourceSetting('ForgedTransmitsInherited', $virtualPortGroupSecurityPolicy.ForgedTransmitsInherited, $this.ForgedTransmitsInherited), $this.ShouldUpdateDscResourceSetting('MacChanges', $virtualPortGroupSecurityPolicy.MacChanges, $this.MacChanges), $this.ShouldUpdateDscResourceSetting('MacChangesInherited', $virtualPortGroupSecurityPolicy.MacChangesInherited, $this.MacChangesInherited) ) return ($shouldUpdateVirtualPortGroupSecurityPolicy -Contains $true) } <# .DESCRIPTION Performs an update on the Security Policy of the specified Virtual Port Group. #> [void] UpdateVirtualPortGroupSecurityPolicy($virtualPortGroupSecurityPolicy) { $securityPolicyParams = @{} $securityPolicyParams.VirtualPortGroupPolicy = $virtualPortGroupSecurityPolicy $this.PopulatePolicySetting($securityPolicyParams, $this.AllowPromiscuousSettingName, $this.AllowPromiscuous, $this.AllowPromiscuousInheritedSettingName, $this.AllowPromiscuousInherited) $this.PopulatePolicySetting($securityPolicyParams, $this.ForgedTransmitsSettingName, $this.ForgedTransmits, $this.ForgedTransmitsInheritedSettingName, $this.ForgedTransmitsInherited) $this.PopulatePolicySetting($securityPolicyParams, $this.MacChangesSettingName, $this.MacChanges, $this.MacChangesInheritedSettingName, $this.MacChangesInherited) try { Set-SecurityPolicy @securityPolicyParams } catch { throw "Cannot update Security Policy of Virtual Port Group $($this.PortGroup). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Security Policy of the specified Virtual Port Group from the server. #> [void] PopulateResult($virtualPortGroupSecurityPolicy, $result) { $result.AllowPromiscuous = $virtualPortGroupSecurityPolicy.AllowPromiscuous $result.AllowPromiscuousInherited = $virtualPortGroupSecurityPolicy.AllowPromiscuousInherited $result.ForgedTransmits = $virtualPortGroupSecurityPolicy.ForgedTransmits $result.ForgedTransmitsInherited = $virtualPortGroupSecurityPolicy.ForgedTransmitsInherited $result.MacChanges = $virtualPortGroupSecurityPolicy.MacChanges $result.MacChangesInherited = $virtualPortGroupSecurityPolicy.MacChangesInherited } } [DscResource()] class VMHostVssPortGroupShaping : VMHostVssPortGroupBaseDSC { <# .DESCRIPTION The flag to indicate whether or not traffic shaper is enabled on the port. #> [DscProperty()] [nullable[bool]] $Enabled <# .DESCRIPTION The average bandwidth in bits per second if shaping is enabled on the port. #> [DscProperty()] [nullable[long]] $AverageBandwidth <# .DESCRIPTION The peak bandwidth during bursts in bits per second if traffic shaping is enabled on the port. #> [DscProperty()] [nullable[long]] $PeakBandwidth <# .DESCRIPTION The maximum burst size allowed in bytes if shaping is enabled on the port. #> [DscProperty()] [nullable[long]] $BurstSize [void] Set() { try { $this.ConnectVIServer() $this.RetrieveVMHost() $this.GetVMHostNetworkSystem() $virtualPortGroup = $this.GetVirtualPortGroup() $this.UpdateVirtualPortGroupShapingPolicy($virtualPortGroup) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $this.RetrieveVMHost() $virtualPortGroup = $this.GetVirtualPortGroup() $result = $null if ($null -eq $virtualPortGroup) { # If the Port Group is $null, it means that Ensure is 'Absent' and the Port Group does not exist. $result = $true } else { $result = !$this.ShouldUpdateVirtualPortGroupShapingPolicy($virtualPortGroup) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostVssPortGroupShaping] Get() { try { $result = [VMHostVssPortGroupShaping]::new() $result.Server = $this.Server $result.Ensure = $this.Ensure $this.ConnectVIServer() $this.RetrieveVMHost() $result.VMHostName = $this.VMHost.Name $virtualPortGroup = $this.GetVirtualPortGroup() if ($null -eq $virtualPortGroup) { # If the Port Group is $null, it means that Ensure is 'Absent' and the Port Group does not exist. $result.Name = $this.Name return $result } $result.Name = $virtualPortGroup.Name $this.PopulateResult($virtualPortGroup, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Checks if the Shaping Policy of the specified Virtual Port Group should be updated. #> [bool] ShouldUpdateVirtualPortGroupShapingPolicy($virtualPortGroup) { $shapingPolicy = $virtualPortGroup.ExtensionData.Spec.Policy.ShapingPolicy $shouldUpdateVirtualPortGroupShapingPolicy = @( $this.ShouldUpdateDscResourceSetting('Enabled', $shapingPolicy.Enabled, $this.Enabled), $this.ShouldUpdateDscResourceSetting('AverageBandwidth', $shapingPolicy.AverageBandwidth, $this.AverageBandwidth), $this.ShouldUpdateDscResourceSetting('PeakBandwidth', $shapingPolicy.PeakBandwidth, $this.PeakBandwidth), $this.ShouldUpdateDscResourceSetting('BurstSize', $shapingPolicy.BurstSize, $this.BurstSize) ) return ($shouldUpdateVirtualPortGroupShapingPolicy -Contains $true) } <# .DESCRIPTION Performs an update on the Shaping Policy of the specified Virtual Port Group. #> [void] UpdateVirtualPortGroupShapingPolicy($virtualPortGroup) { $virtualPortGroupSpec = New-Object VMware.Vim.HostPortGroupSpec $virtualPortGroupSpec.Name = $virtualPortGroup.Name $virtualPortGroupSpec.VswitchName = $virtualPortGroup.VirtualSwitchName $virtualPortGroupSpec.VlanId = $virtualPortGroup.VLanId $virtualPortGroupSpec.Policy = New-Object VMware.Vim.HostNetworkPolicy $virtualPortGroupSpec.Policy.ShapingPolicy = New-Object VMware.Vim.HostNetworkTrafficShapingPolicy if ($null -ne $this.Enabled) { $virtualPortGroupSpec.Policy.ShapingPolicy.Enabled = $this.Enabled } if ($null -ne $this.AverageBandwidth) { $virtualPortGroupSpec.Policy.ShapingPolicy.AverageBandwidth = $this.AverageBandwidth } if ($null -ne $this.PeakBandwidth) { $virtualPortGroupSpec.Policy.ShapingPolicy.PeakBandwidth = $this.PeakBandwidth } if ($null -ne $this.BurstSize) { $virtualPortGroupSpec.Policy.ShapingPolicy.BurstSize = $this.BurstSize } try { Update-VirtualPortGroup -VMHostNetworkSystem $this.VMHostNetworkSystem -VirtualPortGroupName $virtualPortGroup.Name -Spec $virtualPortGroupSpec } catch { throw "Cannot update Shaping Policy of Virtual Port Group $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Shaping Policy of the specified Virtual Port Group from the server. #> [void] PopulateResult($virtualPortGroup, $result) { $result.Enabled = $virtualPortGroup.ExtensionData.Spec.Policy.ShapingPolicy.Enabled $result.AverageBandwidth = $virtualPortGroup.ExtensionData.Spec.Policy.ShapingPolicy.AverageBandwidth $result.PeakBandwidth = $virtualPortGroup.ExtensionData.Spec.Policy.ShapingPolicy.PeakBandwidth $result.BurstSize = $virtualPortGroup.ExtensionData.Spec.Policy.ShapingPolicy.BurstSize } } [DscResource()] class VMHostVssPortGroupTeaming : VMHostVssPortGroupBaseDSC { <# .DESCRIPTION Specifies how a physical adapter is returned to active duty after recovering from a failure. If the value is $true, the adapter is returned to active duty immediately on recovery, displacing the standby adapter that took over its slot, if any. If the value is $false, a failed adapter is left inactive even after recovery until another active adapter fails, requiring its replacement. #> [DscProperty()] [nullable[bool]] $FailbackEnabled <# .DESCRIPTION Determines how network traffic is distributed between the network adapters assigned to a switch. The following values are valid: LoadBalanceIP - Route based on IP hash. Choose an uplink based on a hash of the source and destination IP addresses of each packet. For non-IP packets, whatever is at those offsets is used to compute the hash. LoadBalanceSrcMac - Route based on source MAC hash. Choose an uplink based on a hash of the source Ethernet. LoadBalanceSrcId - Route based on the originating port ID. Choose an uplink based on the virtual port where the traffic entered the virtual switch. ExplicitFailover - Always use the highest order uplink from the list of Active adapters that passes failover detection criteria. #> [DscProperty()] [LoadBalancingPolicy] $LoadBalancingPolicy = [LoadBalancingPolicy]::Unset <# .DESCRIPTION Specifies the adapters you want to continue to use when the network adapter connectivity is available and active. #> [DscProperty()] [string[]] $ActiveNic <# .DESCRIPTION Specifies the adapters you want to use if one of the active adapter's connectivity is unavailable. #> [DscProperty()] [string[]] $StandbyNic <# .DESCRIPTION Specifies the adapters you do not want to use. #> [DscProperty()] [string[]] $UnusedNic <# .DESCRIPTION Specifies how to reroute traffic in the event of an adapter failure. The following values are valid: LinkStatus - Relies solely on the link status that the network adapter provides. This option detects failures, such as cable pulls and physical switch power failures, but not configuration errors, such as a physical switch port being blocked by spanning tree or misconfigured to the wrong VLAN or cable pulls on the other side of a physical switch. BeaconProbing - Sends out and listens for beacon probes on all NICs in the team and uses this information, in addition to link status, to determine link failure. This option detects many of the failures mentioned above that are not detected by link status alone. #> [DscProperty()] [NetworkFailoverDetectionPolicy] $NetworkFailoverDetectionPolicy = [NetworkFailoverDetectionPolicy]::Unset <# .DESCRIPTION Indicates that whenever a virtual NIC is connected to the virtual switch or whenever that virtual NIC's traffic is routed over a different physical NIC in the team because of a failover event, a notification is sent over the network to update the lookup tables on the physical switches. #> [DscProperty()] [nullable[bool]] $NotifySwitches <# .DESCRIPTION Indicates that the value of the FailbackEnabled parameter is inherited from the virtual switch. #> [DscProperty()] [nullable[bool]] $InheritFailback <# .DESCRIPTION Indicates that the values of the ActiveNic, StandbyNic, and UnusedNic parameters are inherited from the virtual switch. #> [DscProperty()] [nullable[bool]] $InheritFailoverOrder <# .DESCRIPTION Indicates that the value of the LoadBalancingPolicy parameter is inherited from the virtual switch. #> [DscProperty()] [nullable[bool]] $InheritLoadBalancingPolicy <# .DESCRIPTION Indicates that the value of the NetworkFailoverDetectionPolicy parameter is inherited from the virtual switch. #> [DscProperty()] [nullable[bool]] $InheritNetworkFailoverDetectionPolicy <# .DESCRIPTION Indicates that the value of the NotifySwitches parameter is inherited from the virtual switch. #> [DscProperty()] [nullable[bool]] $InheritNotifySwitches hidden [string] $FailbackEnabledSettingName = 'FailbackEnabled' hidden [string] $InheritFailbackSettingName = 'InheritFailback' hidden [string] $LoadBalancingPolicySettingName = 'LoadBalancingPolicy' hidden [string] $InheritLoadBalancingPolicySettingName = 'InheritLoadBalancingPolicy' hidden [string] $NetworkFailoverDetectionPolicySettingName = 'NetworkFailoverDetectionPolicy' hidden [string] $InheritNetworkFailoverDetectionPolicySettingName = 'InheritNetworkFailoverDetectionPolicy' hidden [string] $NotifySwitchesSettingName = 'NotifySwitches' hidden [string] $InheritNotifySwitchesSettingName = 'InheritNotifySwitches' hidden [string] $MakeNicActiveSettingName = 'MakeNicActive' hidden [string] $MakeNicStandbySettingName = 'MakeNicStandby' hidden [string] $MakeNicUnusedSettingName = 'MakeNicUnused' hidden [string] $InheritFailoverOrderSettingName = 'InheritFailoverOrder' [void] Set() { try { $this.ConnectVIServer() $this.RetrieveVMHost() $virtualPortGroup = $this.GetVirtualPortGroup() $virtualPortGroupTeamingPolicy = $this.GetVirtualPortGroupTeamingPolicy($virtualPortGroup) $this.UpdateVirtualPortGroupTeamingPolicy($virtualPortGroupTeamingPolicy) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $this.RetrieveVMHost() $virtualPortGroup = $this.GetVirtualPortGroup() $result = $null if ($null -eq $virtualPortGroup) { # If the Port Group is $null, it means that Ensure is 'Absent' and the Port Group does not exist. $result = $true } else { $virtualPortGroupTeamingPolicy = $this.GetVirtualPortGroupTeamingPolicy($virtualPortGroup) $result = !$this.ShouldUpdateVirtualPortGroupTeamingPolicy($virtualPortGroupTeamingPolicy) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostVssPortGroupTeaming] Get() { try { $result = [VMHostVssPortGroupTeaming]::new() $result.Server = $this.Server $result.Ensure = $this.Ensure $this.ConnectVIServer() $this.RetrieveVMHost() $result.VMHostName = $this.VMHost.Name $virtualPortGroup = $this.GetVirtualPortGroup() if ($null -eq $virtualPortGroup) { # If the Port Group is $null, it means that Ensure is 'Absent' and the Port Group does not exist. $result.Name = $this.Name return $result } $virtualPortGroupTeamingPolicy = $this.GetVirtualPortGroupTeamingPolicy($virtualPortGroup) $result.Name = $virtualPortGroup.Name $this.PopulateResult($virtualPortGroupTeamingPolicy, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the Virtual Port Group Teaming Policy from the server. #> [PSObject] GetVirtualPortGroupTeamingPolicy($virtualPortGroup) { try { $virtualPortGroupTeamingPolicy = Get-NicTeamingPolicy -Server $this.Connection -VirtualPortGroup $virtualPortGroup -ErrorAction Stop return $virtualPortGroupTeamingPolicy } catch { throw "Could not retrieve Virtual Port Group $($this.Name) Teaming Policy. For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Checks if the Teaming Policy of the specified Virtual Port Group should be updated. #> [bool] ShouldUpdateVirtualPortGroupTeamingPolicy($virtualPortGroupTeamingPolicy) { $shouldUpdateVirtualPortGroupTeamingPolicy = @( $this.ShouldUpdateDscResourceSetting('FailbackEnabled', $virtualPortGroupTeamingPolicy.FailbackEnabled, $this.FailbackEnabled), $this.ShouldUpdateDscResourceSetting('NotifySwitches', $virtualPortGroupTeamingPolicy.NotifySwitches, $this.NotifySwitches), $this.ShouldUpdateDscResourceSetting('InheritFailback', $virtualPortGroupTeamingPolicy.IsFailbackInherited, $this.InheritFailback), $this.ShouldUpdateDscResourceSetting('InheritFailoverOrder', $virtualPortGroupTeamingPolicy.IsFailoverOrderInherited, $this.InheritFailoverOrder), $this.ShouldUpdateDscResourceSetting('InheritLoadBalancingPolicy', $virtualPortGroupTeamingPolicy.IsLoadBalancingInherited, $this.InheritLoadBalancingPolicy), $this.ShouldUpdateDscResourceSetting( 'InheritNetworkFailoverDetectionPolicy', $virtualPortGroupTeamingPolicy.IsNetworkFailoverDetectionInherited, $this.InheritNetworkFailoverDetectionPolicy ), $this.ShouldUpdateDscResourceSetting('InheritNotifySwitches', $virtualPortGroupTeamingPolicy.IsNotifySwitchesInherited, $this.InheritNotifySwitches), $this.ShouldUpdateDscResourceSetting('LoadBalancingPolicy', [string] $virtualPortGroupTeamingPolicy.LoadBalancingPolicy, $this.LoadBalancingPolicy.ToString()), $this.ShouldUpdateDscResourceSetting( 'NetworkFailoverDetectionPolicy', [string] $virtualPortGroupTeamingPolicy.NetworkFailoverDetectionPolicy, $this.NetworkFailoverDetectionPolicy.ToString() ), $this.ShouldUpdateArraySetting('ActiveNic', $virtualPortGroupTeamingPolicy.ActiveNic, $this.ActiveNic), $this.ShouldUpdateArraySetting('StandbyNic', $virtualPortGroupTeamingPolicy.StandbyNic, $this.StandbyNic), $this.ShouldUpdateArraySetting('UnusedNic', $virtualPortGroupTeamingPolicy.UnusedNic, $this.UnusedNic) ) return ($shouldUpdateVirtualPortGroupTeamingPolicy -Contains $true) } <# .DESCRIPTION Populates the specified Enum Policy Setting. If the Inherited Setting is passed and set to $true, the Policy Setting should not be populated because "Parameters of the form "XXX" and "InheritXXX" are mutually exclusive." If the Inherited Setting is set to $false, both parameters can be populated. #> [void] PopulateEnumPolicySetting($policyParams, $policySettingName, $policySetting, $policySettingInheritedName, $policySettingInherited) { if ($policySetting -ne 'Unset') { if ($null -eq $policySettingInherited -or !$policySettingInherited) { $policyParams.$policySettingName = $policySetting } } if ($null -ne $policySettingInherited) { $policyParams.$policySettingInheritedName = $policySettingInherited } } <# .DESCRIPTION Populates the specified Array Policy Setting. If the Inherited Setting is passed and set to $true, the Policy Setting should not be populated because "Parameters of the form "XXX" and "InheritXXX" are mutually exclusive." If the Inherited Setting is set to $false, both parameters can be populated. #> [void] PopulateArrayPolicySetting($policyParams, $policySettingName, $policySetting, $policySettingInheritedName, $policySettingInherited) { if ($null -ne $policySetting -and $policySetting.Length -gt 0) { if ($null -eq $policySettingInherited -or !$policySettingInherited) { $policyParams.$policySettingName = $policySetting } } if ($null -ne $policySettingInherited) { $policyParams.$policySettingInheritedName = $policySettingInherited } } <# .DESCRIPTION Performs an update on the Teaming Policy of the specified Virtual Port Group. #> [void] UpdateVirtualPortGroupTeamingPolicy($virtualPortGroupTeamingPolicy) { $teamingPolicyParams = @{} $teamingPolicyParams.VirtualPortGroupPolicy = $virtualPortGroupTeamingPolicy $this.PopulatePolicySetting($teamingPolicyParams, $this.FailbackEnabledSettingName, $this.FailbackEnabled, $this.InheritFailbackSettingName, $this.InheritFailback) $this.PopulatePolicySetting($teamingPolicyParams, $this.NotifySwitchesSettingName, $this.NotifySwitches, $this.InheritNotifySwitchesSettingName, $this.InheritNotifySwitches) $this.PopulateEnumPolicySetting($teamingPolicyParams, $this.LoadBalancingPolicySettingName, $this.LoadBalancingPolicy.ToString(), $this.InheritLoadBalancingPolicySettingName, $this.InheritLoadBalancingPolicy) $this.PopulateEnumPolicySetting($teamingPolicyParams, $this.NetworkFailoverDetectionPolicySettingName, $this.NetworkFailoverDetectionPolicy.ToString(), $this.InheritNetworkFailoverDetectionPolicySettingName, $this.InheritNetworkFailoverDetectionPolicy) $this.PopulateArrayPolicySetting($teamingPolicyParams, $this.MakeNicActiveSettingName, $this.ActiveNic, $this.InheritFailoverOrderSettingName, $this.InheritFailoverOrder) $this.PopulateArrayPolicySetting($teamingPolicyParams, $this.MakeNicStandbySettingName, $this.StandbyNic, $this.InheritFailoverOrderSettingName, $this.InheritFailoverOrder) $this.PopulateArrayPolicySetting($teamingPolicyParams, $this.MakeNicUnusedSettingName, $this.UnusedNic, $this.InheritFailoverOrderSettingName, $this.InheritFailoverOrder) try { Set-NicTeamingPolicy @teamingPolicyParams } catch { throw "Cannot update Teaming Policy of Virtual Port Group $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Teaming Policy of the specified Virtual Port Group from the server. #> [void] PopulateResult($virtualPortGroupTeamingPolicy, $result) { $result.FailbackEnabled = $virtualPortGroupTeamingPolicy.FailbackEnabled $result.NotifySwitches = $virtualPortGroupTeamingPolicy.NotifySwitches $result.LoadBalancingPolicy = $virtualPortGroupTeamingPolicy.LoadBalancingPolicy.ToString() $result.NetworkFailoverDetectionPolicy = $virtualPortGroupTeamingPolicy.NetworkFailoverDetectionPolicy.ToString() $result.ActiveNic = $virtualPortGroupTeamingPolicy.ActiveNic $result.StandbyNic = $virtualPortGroupTeamingPolicy.StandbyNic $result.UnusedNic = $virtualPortGroupTeamingPolicy.UnusedNic $result.InheritFailback = $virtualPortGroupTeamingPolicy.IsFailbackInherited $result.InheritNotifySwitches = $virtualPortGroupTeamingPolicy.IsNotifySwitchesInherited $result.InheritLoadBalancingPolicy = $virtualPortGroupTeamingPolicy.IsLoadBalancingInherited $result.InheritNetworkFailoverDetectionPolicy = $virtualPortGroupTeamingPolicy.IsNetworkFailoverDetectionInherited $result.InheritFailoverOrder = $virtualPortGroupTeamingPolicy.IsFailoverOrderInherited } } [DscResource()] class VMHostAcceptanceLevel : EsxCliBaseDSC { VMHostAcceptanceLevel() { $this.EsxCliCommand = 'software.acceptance' } <# .DESCRIPTION Specifies the acceptance level of the VMHost. Valid values are VMwareCertified, VMwareAccepted, PartnerSupported and CommunitySupported. #> [DscProperty(Mandatory)] [AcceptanceLevel] $Level [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) # The 'Level' value needs to be converted to a string type before being passed to the base class method. $modifyVMHostAcceptanceLevelMethodArguments = @{ level = $this.Level.ToString() } $this.ExecuteEsxCliModifyMethod($this.EsxCliSetMethodName, $modifyVMHostAcceptanceLevelMethodArguments) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result = !$this.ShouldUpdateDscResourceSetting('Level', [string] $esxCliGetMethodResult, $this.Level.ToString()) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostAcceptanceLevel] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostAcceptanceLevel]::new() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.PopulateResult($result, $vmHost) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHost) { $result.Server = $this.Connection.Name $result.Name = $vmHost.Name $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result.Level = $esxCliGetMethodResult } } [DscResource()] class VMHostDCUIKeyboard : EsxCliBaseDSC { VMHostDCUIKeyboard() { $this.EsxCliCommand = 'system.settings.keyboard.layout' } <# .DESCRIPTION Specifies the name of the Direct Console User Interface Keyboard Layout. #> [DscProperty(Mandatory)] [string] $Layout [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.ExecuteEsxCliModifyMethod($this.EsxCliSetMethodName) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result = !$this.ShouldUpdateDscResourceSetting('Layout', [string] $esxCliGetMethodResult, $this.Layout) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostDCUIKeyboard] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostDCUIKeyboard]::new() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.PopulateResult($result, $vmHost) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHost) { $result.Server = $this.Connection.Name $result.Name = $vmHost.Name $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result.Layout = $esxCliGetMethodResult } } [DscResource()] class VMHostNetworkCoreDump : EsxCliBaseDSC { VMHostNetworkCoreDump() { $this.EsxCliCommand = 'system.coredump.network' } <# .DESCRIPTION Specifies whether to enable network coredump. #> [DscProperty()] [nullable[bool]] $Enable <# .DESCRIPTION Specifies the active interface to be used for the network coredump. #> [DscProperty()] [string] $InterfaceName <# .DESCRIPTION Specifies the IP address of the coredump server (IPv4 or IPv6). #> [DscProperty()] [string] $ServerIp <# .DESCRIPTION Specifies the port on which the coredump server is listening. #> [DscProperty()] [nullable[long]] $ServerPort [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, ($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) <# The 'Enable' argument of the 'set' method of the command should be passed separately from the other method arguments. So if any of the other method arguments is passed, the method of the command should be invoked twice - the first time without 'Enable' and the second time only with 'Enable' argument. #> if ($null -ne $this.Enable) { if (![string]::IsNullOrEmpty($this.InterfaceName) -or ![string]::IsNullOrEmpty($this.ServerIp) -or $null -ne $this.ServerPort) { <# The desired 'Enable' value must be set to $null, so that the base class can ignore it when constructing the arguments of the method of the command. The value is stored in a separate variable, so that it can be used when the second invocation of the command method occurs. #> $enableArgumentDesiredValue = $this.Enable $this.Enable = $null $this.ExecuteEsxCliModifyMethod($this.EsxCliSetMethodName) # The property value is restored to its initial value. $this.Enable = $enableArgumentDesiredValue # All property values except the desired 'Enable' value should be set to $null, because the command method was invoked with them already and their values are not needed. $this.InterfaceName = $null $this.ServerIp = $null $this.ServerPort = $null $this.ExecuteEsxCliModifyMethod($this.EsxCliSetMethodName) } else { $this.ExecuteEsxCliModifyMethod($this.EsxCliSetMethodName) } } else { $this.ExecuteEsxCliModifyMethod($this.EsxCliSetMethodName) } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, ($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, ($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result = !$this.ShouldModifyVMHostNetworkCoreDumpConfiguration($esxCliGetMethodResult) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, ($this.DscResourceName)) } } [VMHostNetworkCoreDump] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, ($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostNetworkCoreDump]::new() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.PopulateResult($result, $vmHost) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, ($this.DscResourceName)) } } <# .DESCRIPTION Checks if the VMHost network coredump configuration should be modified. #> [bool] ShouldModifyVMHostNetworkCoreDumpConfiguration($esxCliGetMethodResult) { $shouldModifyVMHostNetworkCoreDumpConfiguration = @( $this.ShouldUpdateDscResourceSetting('Enable', [System.Convert]::ToBoolean($esxCliGetMethodResult.Enabled), $this.Enable), $this.ShouldUpdateDscResourceSetting('InterfaceName', [string] $esxCliGetMethodResult.HostVNic, $this.InterfaceName), $this.ShouldUpdateDscResourceSetting('ServerIp', [string] $esxCliGetMethodResult.NetworkServerIP, $this.ServerIp), $this.ShouldUpdateDscResourceSetting('ServerPort', [long] $esxCliGetMethodResult.NetworkServerPort, $this.ServerPort) ) return ($shouldModifyVMHostNetworkCoreDumpConfiguration -Contains $true) } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHost) { $result.Server = $this.Connection.Name $result.Name = $vmHost.Name $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result.Enable = [System.Convert]::ToBoolean($esxCliGetMethodResult.Enabled) $result.InterfaceName = $esxCliGetMethodResult.HostVNic $result.ServerIp = $esxCliGetMethodResult.NetworkServerIP $result.ServerPort = [long] $esxCliGetMethodResult.NetworkServerPort } } [DscResource()] class VMHostSharedSwapSpace : EsxCliBaseDSC { VMHostSharedSwapSpace() { $this.EsxCliCommand = 'sched.swap.system' } <# .DESCRIPTION Specifies if the Datastore option should be enabled or not. #> [DscProperty()] [nullable[bool]] $DatastoreEnabled <# .DESCRIPTION Specifies the name of the Datastore used by the Datastore option. #> [DscProperty()] [string] $DatastoreName <# .DESCRIPTION Specifies the order of the Datastore option in the preference of the options of the system-wide shared swap space. #> [DscProperty()] [nullable[long]] $DatastoreOrder <# .DESCRIPTION Specifies if the host cache option should be enabled or not. #> [DscProperty()] [nullable[bool]] $HostCacheEnabled <# .DESCRIPTION Specifies the order of the host cache option in the preference of the options of the system-wide shared swap space. #> [DscProperty()] [nullable[long]] $HostCacheOrder <# .DESCRIPTION Specifies if the host local swap option should be enabled or not. #> [DscProperty()] [nullable[bool]] $HostLocalSwapEnabled <# .DESCRIPTION Specifies the order of the host local swap option in the preference of the options of the system-wide shared swap space. #> [DscProperty()] [nullable[long]] $HostLocalSwapOrder [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, ($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $modifyVMHostSharedSwapSpaceMethodArguments = @{} if ($null -ne $this.DatastoreName) { $modifyVMHostSharedSwapSpaceMethodArguments.datastorename = $this.DatastoreName } $this.ExecuteEsxCliModifyMethod($this.EsxCliSetMethodName, $modifyVMHostSharedSwapSpaceMethodArguments) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, ($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, ($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result = !$this.ShouldModifySystemWideSharedSwapSpaceConfiguration($esxCliGetMethodResult) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, ($this.DscResourceName)) } } [VMHostSharedSwapSpace] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, ($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostSharedSwapSpace]::new() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.PopulateResult($result, $vmHost) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, ($this.DscResourceName)) } } <# .DESCRIPTION Checks if the system-wide shared swap space configuration should be modified. #> [bool] ShouldModifySystemWideSharedSwapSpaceConfiguration($esxCliGetMethodResult) { $shouldModifySystemWideSharedSwapSpaceConfiguration = @( $this.ShouldUpdateDscResourceSetting('DatastoreEnabled', [System.Convert]::ToBoolean($esxCliGetMethodResult.DatastoreEnabled), $this.DatastoreEnabled), $this.ShouldUpdateDscResourceSetting('DatastoreName', [string] $esxCliGetMethodResult.DatastoreName, $this.DatastoreName), $this.ShouldUpdateDscResourceSetting('DatastoreOrder', [long] $esxCliGetMethodResult.DatastoreOrder, $this.DatastoreOrder), $this.ShouldUpdateDscResourceSetting('HostCacheEnabled', [System.Convert]::ToBoolean($esxCliGetMethodResult.HostcacheEnabled), $this.HostCacheEnabled), $this.ShouldUpdateDscResourceSetting('HostCacheOrder', [long] $esxCliGetMethodResult.HostcacheOrder, $this.HostCacheOrder), $this.ShouldUpdateDscResourceSetting('HostLocalSwapOrder', [long] $esxCliGetMethodResult.HostlocalswapOrder, $this.HostLocalSwapOrder), $this.ShouldUpdateDscResourceSetting( 'HostLocalSwapEnabled', [System.Convert]::ToBoolean($esxCliGetMethodResult.HostlocalswapEnabled), $this.HostLocalSwapEnabled ) ) return ($shouldModifySystemWideSharedSwapSpaceConfiguration -Contains $true) } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHost) { $result.Server = $this.Connection.Name $result.Name = $vmHost.Name $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result.DatastoreEnabled = [System.Convert]::ToBoolean($esxCliGetMethodResult.DatastoreEnabled) $result.DatastoreName = $esxCliGetMethodResult.DatastoreName $result.DatastoreOrder = [long] $esxCliGetMethodResult.DatastoreOrder $result.HostCacheEnabled = [System.Convert]::ToBoolean($esxCliGetMethodResult.HostcacheEnabled) $result.HostCacheOrder = [long] $esxCliGetMethodResult.HostcacheOrder $result.HostLocalSwapEnabled = [System.Convert]::ToBoolean($esxCliGetMethodResult.HostlocalswapEnabled) $result.HostLocalSwapOrder = [long] $esxCliGetMethodResult.HostlocalswapOrder } } [DscResource()] class VMHostSNMPAgent : EsxCliBaseDSC { VMHostSNMPAgent() { $this.EsxCliCommand = 'system.snmp' } <# .DESCRIPTION Specifies the default authentication protocol. Valid values are none, MD5, SHA1. #> [DscProperty()] [string] $Authentication <# .DESCRIPTION Specifies up to ten communities each no more than 64 characters. Format is: 'community1[,community2,...]'. This overwrites previous settings. #> [DscProperty()] [string] $Communities <# .DESCRIPTION Specifies whether to start or stop the SNMP service. #> [DscProperty()] [nullable[bool]] $Enable <# .DESCRIPTION Specifies the SNMPv3 engine id. Must be between 10 and 32 hexadecimal characters. 0x or 0X are stripped if found as well as colons (:). #> [DscProperty()] [string] $EngineId <# .DESCRIPTION Specifies where to source hardware events - IPMI sensors or CIM Indications. Valid values are indications and sensors. #> [DscProperty()] [string] $Hwsrc <# .DESCRIPTION Specifies whether to support large storage for 'hrStorageAllocationUnits' * 'hrStorageSize'. Controls how the agent reports 'hrStorageAllocationUnits', 'hrStorageSize' and 'hrStorageUsed' in 'hrStorageTable'. Setting this directive to $true to support large storage with small allocation units, the agent re-calculates these values so they all fit into 'int' and 'hrStorageAllocationUnits' * 'hrStorageSize' gives real size of the storage. Setting this directive to $false turns off this calculation and the agent reports real 'hrStorageAllocationUnits', but it might report wrong 'hrStorageSize' for large storage because the value won't fit into 'int'. #> [DscProperty()] [nullable[bool]] $LargeStorage <# .DESCRIPTION Specifies the SNMP agent syslog logging level. Valid values are debug, info, warning and error. #> [DscProperty()] [string] $LogLevel <# .DESCRIPTION Specifies a comma separated list of trap oids for traps not to be sent by the SNMP agent. Use the property 'reset' to clear this setting. #> [DscProperty()] [string] $NoTraps <# .DESCRIPTION Specifies the UDP port to poll SNMP agent on. The default is 'udp/161'. May not use ports 32768 to 40959. #> [DscProperty()] [nullable[long]] $Port <# .DESCRIPTION Specifies the default privacy protocol. Valid values are none and AES128. #> [DscProperty()] [string] $Privacy <# .DESCRIPTION Specifies up to five inform user ids. Format is: 'user/auth-proto/-|auth-hash/priv-proto/-|priv-hash/engine-id[,...]', where user is 32 chars max. 'auth-proto' is 'none', 'MD5' or 'SHA1', 'priv-proto' is 'none' or 'AES'. '-' indicates no hash. 'engine-id' is hex string '0x0-9a-f' up to 32 chars max. #> [DscProperty()] [string] $RemoteUsers <# .DESCRIPTION Specifies whether to return SNMP agent configuration to factory defaults. #> [DscProperty()] [nullable[bool]] $Reset <# .DESCRIPTION Specifies the System contact as presented in 'sysContact.0'. Up to 255 characters. #> [DscProperty()] [string] $SysContact <# .DESCRIPTION Specifies the System location as presented in 'sysLocation.0'. Up to 255 characters. #> [DscProperty()] [string] $SysLocation <# .DESCRIPTION Specifies up to three targets to send SNMPv1 traps to. Format is: 'ip-or-hostname[@port]/community[,...]'. The default port is 'udp/162'. #> [DscProperty()] [string] $Targets <# .DESCRIPTION Specifies up to five local users. Format is: 'user/-|auth-hash/-|priv-hash/model[,...]', where user is 32 chars max. '-' indicates no hash. Model is one of 'none', 'auth' or 'priv'. #> [DscProperty()] [string] $Users <# .DESCRIPTION Specifies up to three SNMPv3 notification targets. Format is: 'ip-or-hostname[@port]/remote-user/security-level/trap|inform[,...]'. #> [DscProperty()] [string] $V3Targets [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $modifyVMHostSNMPAgentMethodArguments = @{} if ($null -ne $this.NoTraps) { $modifyVMHostSNMPAgentMethodArguments.notraps = $this.NoTraps } if ($null -ne $this.SysContact) { $modifyVMHostSNMPAgentMethodArguments.syscontact = $this.SysContact } if ($null -ne $this.SysLocation) { $modifyVMHostSNMPAgentMethodArguments.syslocation = $this.SysLocation } $this.ExecuteEsxCliModifyMethod($this.EsxCliSetMethodName, $modifyVMHostSNMPAgentMethodArguments) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result = !$this.ShouldModifyVMHostSNMPAgent($esxCliGetMethodResult) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostSNMPAgent] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostSNMPAgent]::new() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.PopulateResult($result, $vmHost) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Checks if the VMHost SNMP Agent should be modified. #> [bool] ShouldModifyVMHostSNMPAgent($esxCliGetMethodResult) { $shouldModifyVMHostSNMPAgent = @( $this.ShouldUpdateDscResourceSetting('Authentication', [string] $esxCliGetMethodResult.authentication, $this.Authentication), $this.ShouldUpdateDscResourceSetting('Communities', [string] $esxCliGetMethodResult.communities, $this.Communities), $this.ShouldUpdateDscResourceSetting('Enable', [System.Convert]::ToBoolean($esxCliGetMethodResult.enable), $this.Enable), $this.ShouldUpdateDscResourceSetting('EngineId', [string] $esxCliGetMethodResult.engineid, $this.EngineId), $this.ShouldUpdateDscResourceSetting('Hwsrc', [string] $esxCliGetMethodResult.hwsrc, $this.Hwsrc), $this.ShouldUpdateDscResourceSetting('LargeStorage', [System.Convert]::ToBoolean($esxCliGetMethodResult.largestorage), $this.LargeStorage), $this.ShouldUpdateDscResourceSetting('LogLevel', [string] $esxCliGetMethodResult.loglevel, $this.LogLevel), $this.ShouldUpdateDscResourceSetting('NoTraps', [string] $esxCliGetMethodResult.notraps, $this.NoTraps), $this.ShouldUpdateDscResourceSetting('Port', [int] $esxCliGetMethodResult.port, $this.Port), $this.ShouldUpdateDscResourceSetting('Privacy', [string] $esxCliGetMethodResult.privacy, $this.Privacy), $this.ShouldUpdateDscResourceSetting('RemoteUsers', [string] $esxCliGetMethodResult.remoteusers, $this.RemoteUsers), $this.ShouldUpdateDscResourceSetting('SysContact', [string] $esxCliGetMethodResult.syscontact, $this.SysContact), $this.ShouldUpdateDscResourceSetting('SysLocation', [string] $esxCliGetMethodResult.syslocation, $this.SysLocation), $this.ShouldUpdateDscResourceSetting('Targets', [string] $esxCliGetMethodResult.targets, $this.Targets), $this.ShouldUpdateDscResourceSetting('Users', [string] $esxCliGetMethodResult.users, $this.Users), $this.ShouldUpdateDscResourceSetting('V3Targets', [string] $esxCliGetMethodResult.v3targets, $this.V3Targets), $this.ShouldUpdateDscResourceSetting('Reset', $false, $this.Reset) ) return ($shouldModifyVMHostSNMPAgent -Contains $true) } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHost) { $result.Server = $this.Connection.Name $result.Name = $vmHost.Name $result.Reset = $this.Reset $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result.Authentication = $esxCliGetMethodResult.authentication $result.Communities = $esxCliGetMethodResult.communities $result.Enable = [System.Convert]::ToBoolean($esxCliGetMethodResult.enable) $result.EngineId = $esxCliGetMethodResult.engineid $result.Hwsrc = $esxCliGetMethodResult.hwsrc $result.LargeStorage = [System.Convert]::ToBoolean($esxCliGetMethodResult.largestorage) $result.LogLevel = $esxCliGetMethodResult.loglevel $result.NoTraps = $esxCliGetMethodResult.notraps $result.Port = [int] $esxCliGetMethodResult.port $result.Privacy = $esxCliGetMethodResult.privacy $result.RemoteUsers = $esxCliGetMethodResult.remoteusers $result.SysContact = $esxCliGetMethodResult.syscontact $result.SysLocation = $esxCliGetMethodResult.syslocation $result.Targets = $esxCliGetMethodResult.targets $result.Users = $esxCliGetMethodResult.users $result.V3Targets = $esxCliGetMethodResult.v3targets } } [DscResource()] class VMHostSoftwareDevice : EsxCliBaseDSC { VMHostSoftwareDevice() { $this.EsxCliCommand = 'device.software' } <# .DESCRIPTION Specifies the device identifier from the device specification for the software device driver. Valid input is in reverse domain name format (e.g. com.company.device...). #> [DscProperty(Key)] [string] $DeviceIdentifier <# .DESCRIPTION Specifies whether the software device should be present or absent. #> [DscProperty(Mandatory = $true)] [Ensure] $Ensure <# .DESCRIPTION Specifies the unique number to address this instance of the device, if multiple instances of the same device identifier are added. Valid values are integer in the range 0-31. Default is 0. #> [DscProperty()] [nullable[long]] $InstanceAddress [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $softwareDevice = $this.GetVMHostSoftwareDevice() if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $softwareDevice) { $this.ExecuteEsxCliModifyMethod($this.EsxCliAddMethodName) } } else { if ($null -ne $softwareDevice) { $this.ExecuteEsxCliModifyMethod($this.EsxCliRemoveMethodName) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $result = $this.IsVMHostSoftwareDeviceInDesiredState() $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostSoftwareDevice] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostSoftwareDevice]::new() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.PopulateResult($result, $vmHost) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the Software device with the specified id and instance address if it exists. #> [PSObject] GetVMHostSoftwareDevice() { $esxCliListMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliListMethodName) $softwareDeviceInstanceAddress = if ($null -ne $this.InstanceAddress) { $this.InstanceAddress } else { 0 } $softwareDevice = $esxCliListMethodResult | Where-Object -FilterScript { $_.DeviceID -eq $this.DeviceIdentifier -and [long] $_.Instance -eq $softwareDeviceInstanceAddress } return $softwareDevice } <# .DESCRIPTION Checks if the Software device is in a Desired State depending on the value of the 'Ensure' property. #> [bool] IsVMHostSoftwareDeviceInDesiredState() { $softwareDevice = $this.GetVMHostSoftwareDevice() $result = $false if ($this.Ensure -eq [Ensure]::Present) { $result = ($null -ne $softwareDevice) } else { $result = ($null -eq $softwareDevice) } return $result } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHost) { $result.Server = $this.Connection.Name $result.Name = $vmHost.Name $softwareDevice = $this.GetVMHostSoftwareDevice() if ($null -ne $softwareDevice) { $result.DeviceIdentifier = $softwareDevice.DeviceID $result.InstanceAddress = [long] $softwareDevice.Instance $result.Ensure = [Ensure]::Present } else { $result.DeviceIdentifier = $this.DeviceIdentifier $result.InstanceAddress = $this.InstanceAddress $result.Ensure = [Ensure]::Absent } } } [DscResource()] class VMHostVMKernelActiveDumpFile : EsxCliBaseDSC { VMHostVMKernelActiveDumpFile() { $this.EsxCliCommand = 'system.coredump.file' } <# .DESCRIPTION Specifies whether the VMKernel dump file should be enabled or disabled. #> [DscProperty()] [nullable[bool]] $Enable <# .DESCRIPTION Specifies whether to select the best available file using the smart selection algorithm. Can only be used when 'Enabled' property is specified with '$true' value. #> [DscProperty()] [nullable[bool]] $Smart [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.ExecuteEsxCliModifyMethod($this.EsxCliSetMethodName) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result = !$this.ShouldModifyVMKernelDumpFile($esxCliGetMethodResult) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostVMKernelActiveDumpFile] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostVMKernelActiveDumpFile]::new() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.PopulateResult($result, $vmHost) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Checks if the VMKernel dump file should be modified. #> [bool] ShouldModifyVMKernelDumpFile($esxCliGetMethodResult) { $result = $null if ($null -ne $this.Enable) { if ($this.Enable) { $result = [string]::IsNullOrEmpty($esxCliGetMethodResult.Active) } else { $result = ![string]::IsNullOrEmpty($esxCliGetMethodResult.Active) } } else { $result = $false } return $result } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHost) { $result.Server = $this.Connection.Name $result.Name = $vmHost.Name $result.Smart = $this.Smart $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result.Enable = ![string]::IsNullOrEmpty($esxCliGetMethodResult.Active) } } [DscResource()] class VMHostVMKernelActiveDumpPartition : EsxCliBaseDSC { VMHostVMKernelActiveDumpPartition() { $this.EsxCliCommand = 'system.coredump.partition' } <# .DESCRIPTION Specifies whether the VMKernel dump partition should be enabled or disabled. #> [DscProperty()] [nullable[bool]] $Enable <# .DESCRIPTION Specifies whether to select the best available partition using the smart selection algorithm. Can only be used when 'Enabled' property is specified with '$true' value. #> [DscProperty()] [nullable[bool]] $Smart [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.ExecuteEsxCliModifyMethod($this.EsxCliSetMethodName) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result = !$this.ShouldModifyVMKernelDumpPartition($esxCliGetMethodResult) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostVMKernelActiveDumpPartition] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostVMKernelActiveDumpPartition]::new() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.PopulateResult($result, $vmHost) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Checks if the VMKernel dump partition should be modified. #> [bool] ShouldModifyVMKernelDumpPartition($esxCliGetMethodResult) { $result = $null if ($null -ne $this.Enable) { if ($this.Enable) { $result = [string]::IsNullOrEmpty($esxCliGetMethodResult.Active) } else { $result = ![string]::IsNullOrEmpty($esxCliGetMethodResult.Active) } } else { $result = $false } return $result } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHost) { $result.Server = $this.Connection.Name $result.Name = $vmHost.Name $result.Smart = $this.Smart $esxCliGetMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliGetMethodName) $result.Enable = ![string]::IsNullOrEmpty($esxCliGetMethodResult.Active) } } [DscResource()] class VMHostVMKernelDumpFile : EsxCliBaseDSC { VMHostVMKernelDumpFile() { $this.EsxCliCommand = 'system.coredump.file' } <# .DESCRIPTION Specifies the name of the Datastore for the dump file. #> [DscProperty(Key)] [string] $DatastoreName <# .DESCRIPTION Specifies the file name of the dump file. #> [DscProperty(Key)] [string] $FileName <# .DESCRIPTION Specifies whether the VMKernel dump Vmfs file should be present or absent. #> [DscProperty(Mandatory = $true)] [Ensure] $Ensure <# .DESCRIPTION Specifies the size in MB of the dump file. If not provided, a default size for the current machine is calculated. #> [DscProperty()] [nullable[long]] $Size <# .DESCRIPTION Specifies whether to deactivate and unconfigure the dump file being removed. This option is required if the file is active. #> [DscProperty()] [nullable[bool]] $Force hidden [string] $CouldNotRetrieveFileSystemsInformationMessage = "Could not retrieve information about File Systems on VMHost {0}. For more information: {1}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) if ($this.Ensure -eq [Ensure]::Present) { $addVMKernelDumpFileMethodArguments = @{ datastore = $this.DatastoreName file = $this.FileName } $this.ExecuteEsxCliModifyMethod($this.EsxCliAddMethodName, $addVMKernelDumpFileMethodArguments) } else { $esxCliListMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliListMethodName) $vmKernelDumpFile = $this.GetVMKernelDumpFile($esxCliListMethodResult) $removeVMKernelDumpFileMethodArguments = @{ file = $vmKernelDumpFile.Path } $this.ExecuteEsxCliModifyMethod($this.EsxCliRemoveMethodName, $removeVMKernelDumpFileMethodArguments) } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $esxCliListMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliListMethodName) $vmKernelDumpFile = $this.GetVMKernelDumpFile($esxCliListMethodResult) $result = $null if ($this.Ensure -eq [Ensure]::Present) { $result = ($vmKernelDumpFile.Count -ne 0) } else { $result = ($vmKernelDumpFile.Count -eq 0) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostVMKernelDumpFile] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostVMKernelDumpFile]::new() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.PopulateResult($result, $vmHost) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Translates the Datastore name from a volume UUID to volume name, if required. #> [string] TranslateDatastoreName($datastoreName) { $foundDatastoreName = $null $fileSystemsList = $null try { $fileSystemsList = Invoke-EsxCliCommandMethod -EsxCli $this.EsxCli -EsxCliCommandMethod 'storage.filesystem.list.Invoke({0})' -EsxCliCommandMethodArguments @{} } catch { throw ($this.CouldNotRetrieveFileSystemsInformationMessage -f $this.Name, $_.Exception.Message) } foreach ($fileSystem in $fileSystemsList) { if ($fileSystem.UUID -eq $datastoreName) { $foundDatastoreName = $fileSystem.VolumeName break } if ($fileSystem.VolumeName -eq $datastoreName) { $foundDatastoreName = $fileSystem.VolumeName break } } return $foundDatastoreName } <# .DESCRIPTION Retrieves the name of the specified dump file. #> [string] GetDumpFileName($dumpFile) { $fileParts = $dumpFile -Split '\.' return $fileParts[0] } <# .DESCRIPTION Converts the passed bytes value to MB value. #> [double] ConvertBytesValueToMBValue($bytesValue) { return [Math]::Round($bytesValue / 1MB) } <# .DESCRIPTION Retrieves the VMKernel dump file if it exists. #> [PSObject] GetVMKernelDumpFile($esxCliListMethodResult) { $foundDumpFile = @{} $result = @() foreach ($dumpFile in $esxCliListMethodResult) { $dumpFileParts = $dumpFile.Path -Split '/' $dumpFileDatastoreName = $this.TranslateDatastoreName($dumpFileParts[3]) $dumpFileName = $this.GetDumpFileName($dumpFileParts[5]) $result += ($this.DatastoreName -eq $dumpFileDatastoreName) $result += ($this.FileName -eq $dumpFileName) if ($null -ne $this.Size) { $result += ($this.Size -eq $this.ConvertBytesValueToMBValue($dumpFile.Size)) } if ($result -NotContains $false) { $foundDumpFile.Path = $dumpFile.Path $foundDumpFile.Datastore = $dumpFileDatastoreName $foundDumpFile.File = $dumpFileName $foundDumpFile.Size = $this.ConvertBytesValueToMBValue($dumpFile.Size) break } $result = @() } return $foundDumpFile } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHost) { $result.Server = $this.Connection.Name $result.Name = $vmHost.Name $result.Force = $this.Force $esxCliListMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliListMethodName) $vmKernelDumpFile = $this.GetVMKernelDumpFile($esxCliListMethodResult) if ($vmKernelDumpFile.Count -ne 0) { $result.DatastoreName = $vmKernelDumpFile.Datastore $result.FileName = $vmKernelDumpFile.File $result.Size = $vmKernelDumpFile.Size $result.Ensure = [Ensure]::Present } else { $result.DatastoreName = $this.DatastoreName $result.FileName = $this.FileName $result.Size = $this.Size $result.Ensure = [Ensure]::Absent } } } [DscResource()] class VMHostVMKernelModule : EsxCliBaseDSC { VMHostVMKernelModule() { $this.EsxCliCommand = 'system.module' } <# .DESCRIPTION Specifies the name of the VMKernel module. #> [DscProperty(Key)] [string] $Module <# .DESCRIPTION Specifies whether the module should be enabled or disabled. #> [DscProperty(Mandatory)] [bool] $Enabled <# .DESCRIPTION Specifies whether to skip the VMkernel module validity checks. #> [DscProperty()] [nullable[bool]] $Force [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.ExecuteEsxCliModifyMethod($this.EsxCliSetMethodName) } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $esxCliListMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliListMethodName) $result = !$this.ShouldModifyVMKernelModule($esxCliListMethodResult) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostVMKernelModule] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostVMKernelModule]::new() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.PopulateResult($result, $vmHost) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Checks if the specified VMKernel module should be modified. #> [bool] ShouldModifyVMKernelModule($esxCliListMethodResult) { $vmKernelModule = $esxCliListMethodResult | Where-Object -FilterScript { $_.Name -eq $this.Module } return $this.ShouldUpdateDscResourceSetting('Enabled', [System.Convert]::ToBoolean($vmKernelModule.IsEnabled), $this.Enabled) } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHost) { $result.Server = $this.Connection.Name $result.Name = $vmHost.Name $result.Force = $this.Force $esxCliListMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliListMethodName) $vmKernelModule = $esxCliListMethodResult | Where-Object -FilterScript { $_.Name -eq $this.Module } $result.Module = $vmKernelModule.Name $result.Enabled = [System.Convert]::ToBoolean($vmKernelModule.IsEnabled) } } [DscResource()] class VMHostvSANNetworkConfiguration : EsxCliBaseDSC { VMHostvSANNetworkConfiguration() { $this.EsxCliCommand = 'vsan.network.ip' } <# .DESCRIPTION Specifies the name of the interface. #> [DscProperty(Key)] [string] $InterfaceName <# .DESCRIPTION Specifies whether the IP interface of the vSAN network configuration should be present or absent. #> [DscProperty(Mandatory = $true)] [Ensure] $Ensure <# .DESCRIPTION Specifies the IPv4 multicast address for the agent group. #> [DscProperty()] [string] $AgentMcAddr <# .DESCRIPTION Specifies the IPv6 multicast address for the agent group. #> [DscProperty()] [string] $AgentV6McAddr <# .DESCRIPTION Specifies the multicast address port for the agent group. #> [DscProperty()] [nullable[long]] $AgentMcPort <# .DESCRIPTION Specifies the unicast address port for the VMHost unicast channel. #> [DscProperty()] [nullable[long]] $HostUcPort <# .DESCRIPTION Specifies the IPv4 multicast address for the master group. #> [DscProperty()] [string] $MasterMcAddr <# .DESCRIPTION Specifies the IPv6 multicast address for the master group. #> [DscProperty()] [string] $MasterV6McAddr <# .DESCRIPTION Specifies the multicast address port for the master group. #> [DscProperty()] [nullable[long]] $MasterMcPort <# .DESCRIPTION Specifies the time-to-live for multicast packets. #> [DscProperty()] [nullable[long]] $MulticastTtl <# .DESCRIPTION Specifies the network transmission type of the vSAN traffic through a virtual network adapter. Supported values are vsan and witness. Type 'vsan' means general vSAN transmission, which is used for both data and witness transmission, if there is no virtual adapter configured with 'witness' traffic type; Type 'witness' indicates that, vSAN vmknic is used for vSAN witness transmission. Once a virtual adapter is configured with 'witness' traffic type, vSAN witness data transmission will stop using virtual adapter with 'vsan' traffic type, and use first dicovered virtual adapter with 'witness' traffic type. Multiple traffic types can be provided in format -T type1 -T type2. Default value is 'vsan', if the property is not specified. #> [DscProperty()] [string[]] $TrafficType <# .DESCRIPTION Specifies whether to notify vSAN subsystem of the removal of the IP Interface, even if is not configured. #> [DscProperty()] [nullable[bool]] $Force [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $vSanNetworkConfigurationIPInterface = $this.GetvSanNetworkConfigurationIPInterface() if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $vSanNetworkConfigurationIPInterface) { $this.ExecuteEsxCliModifyMethod($this.EsxCliAddMethodName) } } else { if ($null -ne $vSanNetworkConfigurationIPInterface) { $this.ExecuteEsxCliModifyMethod($this.EsxCliRemoveMethodName) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $result = $this.IsvSanNetworkConfigurationIPInterfaceInDesiredState() $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostvSANNetworkConfiguration] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostvSANNetworkConfiguration]::new() $vmHost = $this.GetVMHost() $this.GetEsxCli($vmHost) $this.PopulateResult($result, $vmHost) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the IP interface with the specified name of the vSAN network configuration. #> [PSObject] GetvSanNetworkConfigurationIPInterface() { <# The 'list' method of the command is not on the same element as the 'add' and 'remove' methods. So the different methods need to be executed with different commands. #> $initialEsxCliCommand = $this.EsxCliCommand $this.EsxCliCommand = 'vsan.network' $esxCliListMethodResult = $this.ExecuteEsxCliRetrievalMethod($this.EsxCliListMethodName) # The command needs to be restored to its initial value, so that it can be used by the 'add' and 'remove' methods. $this.EsxCliCommand = $initialEsxCliCommand return ($esxCliListMethodResult | Where-Object -FilterScript { $_.VmkNicName -eq $this.InterfaceName }) } <# .DESCRIPTION Checks if the vSan network configuration IP interface is in a Desired State depending on the value of the 'Ensure' property. #> [bool] IsvSanNetworkConfigurationIPInterfaceInDesiredState() { $vSanNetworkConfigurationIPInterface = $this.GetvSanNetworkConfigurationIPInterface() $result = $false if ($this.Ensure -eq [Ensure]::Present) { $result = ($null -ne $vSanNetworkConfigurationIPInterface) } else { $result = ($null -eq $vSanNetworkConfigurationIPInterface) } return $result } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHost) { $result.Server = $this.Connection.Name $result.Name = $vmHost.Name $result.Force = $this.Force $vSanNetworkConfigurationIPInterface = $this.GetvSanNetworkConfigurationIPInterface() if ($null -ne $vSanNetworkConfigurationIPInterface) { $result.InterfaceName = $vSanNetworkConfigurationIPInterface.VmkNicName $result.AgentMcAddr = $vSanNetworkConfigurationIPInterface.AgentGroupMulticastAddress $result.AgentMcPort = [long] $vSanNetworkConfigurationIPInterface.AgentGroupMulticastPort $result.AgentV6McAddr = $vSanNetworkConfigurationIPInterface.AgentGroupIPv6MulticastAddress $result.HostUcPort = [long] $vSanNetworkConfigurationIPInterface.HostUnicastChannelBoundPort $result.MasterMcAddr = $vSanNetworkConfigurationIPInterface.MasterGroupMulticastAddress $result.MasterMcPort = [long] $vSanNetworkConfigurationIPInterface.MasterGroupMulticastPort $result.MasterV6McAddr = $vSanNetworkConfigurationIPInterface.MasterGroupIPv6MulticastAddress $result.MulticastTtl = [long] $vSanNetworkConfigurationIPInterface.MulticastTTL $result.TrafficType = $vSanNetworkConfigurationIPInterface.TrafficType $result.Ensure = [Ensure]::Present } else { $result.InterfaceName = $this.InterfaceName $result.AgentMcAddr = $this.AgentMcAddr $result.AgentMcPort = $this.AgentMcPort $result.AgentV6McAddr = $this.AgentV6McAddr $result.HostUcPort = $this.HostUcPort $result.MasterMcAddr = $this.MasterMcAddr $result.MasterMcPort = $this.MasterMcPort $result.MasterV6McAddr = $this.MasterV6McAddr $result.MulticastTtl = $this.MulticastTtl $result.TrafficType = $this.TrafficType $result.Ensure = [Ensure]::Absent } } } [DscResource()] class VMHostVDSwitchMigration : VMHostNetworkMigrationBaseDSC { <# .DESCRIPTION Specifies the name of the vSphere Distributed Switch to which the VMHost and its Network should be part of. VMHost Network consists of the passed Physical Network Adapters, VMKernel Network Adapters and Port Groups. #> [DscProperty(Key)] [string] $VdsName <# .DESCRIPTION Specifies the names of the Port Groups to which the specified VMKernel Network Adapters should be attached. Accepts either one Port Group or the same number of Port Groups as the number of VMKernel Network Adapters specified. If one Port Group is specified, all Adapters are attached to that Port Group. If the same number of Port Groups as the number of VMKernel Network Adapters are specified, the first Adapter is attached to the first Port Group, the second Adapter to the second Port Group, and so on. #> [DscProperty()] [string[]] $PortGroupNames <# .DESCRIPTION Specifies whether the user wants to migrate only the Physical Network Adapters when no VMKernel Network Adapters are specified. Migrating a Physical Network Adapter that takes care of the Management traffic without a VMKernel Network Adapter could result in an ESXi network connectivity loss. #> [DscProperty()] [nullable[bool]] $MigratePhysicalNicsOnly hidden [string] $RetrieveVDSwitchMessage = "Retrieving VDSwitch {0} from vCenter {1}." hidden [string] $RetrieveStandardPortGroupMessage = "Retrieving Standard Port Group {0} located on VMHost {1}." hidden [string] $CreateVDPortGroupMessage = "Creating VDPortGroup {0} on VDSwitch {1}{2}" hidden [string] $AddVDSwitchToVMHostMessage = "Adding VDSwitch {0} to VMHost {1}." hidden [string] $AddPhysicalNicsToVDSwitchMessage = "Migrating Physical Network Adapters {0} to VDSwitch {1}." hidden [string] $AddPhysicalNicsAndVMKernelNicsToVDSwitchMessage = "Migrating Physical Network Adapters {0} and VMKernel Network Adapters {1} to VDSwitch {2}." hidden [string] $MigratePhysicalNicsOnlyNotSpecified = "When migrating Physical Network Adapters without VMKernel Network Adapters, the MigratePhysicalNicsOnly parameter should be specified in order for the migration to occur." hidden [string] $CouldNotRetrieveVDSwitchMessage = "Could not retrieve VDSwitch {0}. For more information: {1}" hidden [string] $CouldNotRetrieveStandardPortGroupMessage = "Could not retrieve Standard Port Group {0} located on VMHost {1}. For more information: {2}" hidden [string] $CouldNotCreateVDPortGroupMessage = "Could not create VDPortGroup {0} on VDSwitch {1}. For more information: {2}" hidden [string] $CouldNotAddVDSwitchToVMHostMessage = "Could not add VDSwitch {0} to VMHost {1}. For more information: {2}" hidden [string] $CouldNotAddPhysicalNicsToVDSwitchMessage = "Could not migrate Physical Network Adapters {0} to VDSwitch {1}. For more information: {2}" hidden [string] $CouldNotAddPhysicalNicsAndVMKernelNicsToVDSwitchMessage = "Could not migrate Physical Network Adapters {0} and VMKernel Network Adapters {1} to VDSwitch {2}. For more information: {3}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $this.RetrieveVMHost() $distributedSwitch = $this.GetDistributedSwitch() if (!$this.IsVMHostAddedToDistributedSwitch($distributedSwitch)) { $this.AddVMHostToDistributedSwitch($distributedSwitch) } $physicalNetworkAdapters = $this.GetPhysicalNetworkAdapters() if ($this.VMkernelNicNames.Length -eq 0) { $this.AddPhysicalNetworkAdaptersToDistributedSwitch($physicalNetworkAdapters, $distributedSwitch) } else { $vmKernelNetworkAdapters = $this.GetVMKernelNetworkAdapters() $this.EnsureVMKernelNetworkAdapterAndPortGroupNamesCountIsCorrect() $this.AddPhysicalNetworkAdaptersAndVMKernelNetworkAdaptersToDistributedSwitch($physicalNetworkAdapters, $vmKernelNetworkAdapters, $distributedSwitch) } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $this.RetrieveVMHost() $distributedSwitch = $this.GetDistributedSwitch() $result = $null if (!$this.IsVMHostAddedToDistributedSwitch($distributedSwitch)) { $result = $false } # The $null checks ensure that the desired state has not been determined yet from the previous statements. if ($null -eq $result -and $this.ShouldAddPhysicalNetworkAdaptersToDistributedSwitch($distributedSwitch)) { $result = $false } if ($null -eq $result -and $this.VMkernelNicNames.Length -eq 0 -and $this.PortGroupNames.Length -eq 0) { $result = $true } elseif ($null -eq $result) { $result = !$this.ShouldAddVMKernelNetworkAdaptersAndPortGroupsToDistributedSwitch($distributedSwitch) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostVDSwitchMigration] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostVDSwitchMigration]::new() $this.EnsureConnectionIsvCenter() $this.RetrieveVMHost() $distributedSwitch = $this.GetDistributedSwitch() $this.PopulateResult($distributedSwitch, $result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the Distributed Switch with the specified name from the server if it exists. Otherwise it throws an exception. #> [PSObject] GetDistributedSwitch() { <# The Verbose logic here is needed to suppress the Exporting and Importing of the cmdlets from the VMware.VimAutomation.Vds Module. #> $savedVerbosePreference = $global:VerbosePreference $global:VerbosePreference = 'SilentlyContinue' try { $this.WriteLogUtil('Verbose', $this.RetrieveVDSwitchMessage, @($this.VdsName, $this.Connection.Name)) $getVDSwitchParams = @{ Server = $this.Connection Name = $this.VdsName ErrorAction = 'Stop' Verbose = $false } return Get-VDSwitch @getVDSwitchParams } catch { throw ($this.CouldNotRetrieveVDSwitchMessage -f $this.VdsName, $_.Exception.Message) } finally { $global:VerbosePreference = $savedVerbosePreference } } <# .DESCRIPTION Retrieves the Standard Port Group with the specified name if it exists. Otherwise it throws an exception. #> [PSObject] GetStandardPortGroup($standardPortGroupName) { try { $this.WriteLogUtil('Verbose', $this.RetrieveStandardPortGroupMessage, @($standardPortGroupName, $this.VMHost.Name)) $getVirtualPortGroupParams = @{ Server = $this.Connection Name = $standardPortGroupName VMHost = $this.VMHost Standard = $true ErrorAction = 'Stop' Verbose = $false } return Get-VirtualPortGroup @getVirtualPortGroupParams } catch { throw ($this.CouldNotRetrieveStandardPortGroupMessage -f $standardPortGroupName, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Creates a hashtable containing the parameters for the Add-VDSwitchPhysicalNetworkAdapter cmdlet. #> [hashtable] GetAddVDSwitchPhysicalNetworkAdapterParams($distributedSwitch, $physicalNics) { return @{ Server = $this.Connection DistributedSwitch = $distributedSwitch VMHostPhysicalNic = $physicalNics Confirm = $false ErrorAction = 'Stop' Verbose = $false } } <# .DESCRIPTION Creates a hashtable containing the parameters for the Add-VDSwitchPhysicalNetworkAdapter cmdlet. #> [hashtable] GetAddVDSwitchPhysicalNetworkAdapterParams($distributedSwitch, $physicalNics, $vmKernelNics, $portGroups) { $addVDSwitchPhysicalNetworkAdapterParams = $this.GetAddVDSwitchPhysicalNetworkAdapterParams($distributedSwitch, $physicalNics) $addVDSwitchPhysicalNetworkAdapterParams.VMHostVirtualNic = $vmKernelNics $addVDSwitchPhysicalNetworkAdapterParams.VirtualNicPortgroup = $portGroups return $addVDSwitchPhysicalNetworkAdapterParams } <# .DESCRIPTION Checks if the specified VMHost is part of the Distributed Switch. #> [bool] IsVMHostAddedToDistributedSwitch($distributedSwitch) { $addedVMHost = $this.VMHost.ExtensionData.Config.Network.ProxySwitch | Where-Object -FilterScript { $_.DvsName -eq $distributedSwitch.Name } return ($null -ne $addedVMHost) } <# .DESCRIPTION Checks if all passed Physical Network Adapters are added to the Distributed Switch. #> [bool] ShouldAddPhysicalNetworkAdaptersToDistributedSwitch($distributedSwitch) { $physicalNetworkAdapters = $this.GetPhysicalNetworkAdapters() if ($physicalNetworkAdapters.Length -eq 0) { throw 'At least one Physical Network Adapter needs to be specified.' } if ($null -eq $distributedSwitch.ExtensionData.Config.Host.Config.Backing.PnicSpec) { # No Physical Network Adapters are added to the Distributed Switch. return $true } foreach ($physicalNetworkAdapter in $physicalNetworkAdapters) { $addedPhysicalNetworkAdapter = $distributedSwitch.ExtensionData.Config.Host.Config.Backing.PnicSpec | Where-Object -FilterScript { $_.PNicDevice -eq $physicalNetworkAdapter.Name } if ($null -eq $addedPhysicalNetworkAdapter) { return $true } } return $false } <# .DESCRIPTION Checks if all passed VMKernel Network Adapters and Port Groups are added to the Distributed Switch. #> [bool] ShouldAddVMKernelNetworkAdaptersAndPortGroupsToDistributedSwitch($distributedSwitch) { $this.EnsureVMKernelNetworkAdapterAndPortGroupNamesCountIsCorrect() if ($this.PortGroupNames.Length -eq 1) { $portGroupName = $this.PortGroupNames[0] foreach ($vmKernelNetworkAdapterName in $this.VMKernelNicNames) { $getVMHostNetworkAdapterParams = @{ Server = $this.Connection Name = $vmKernelNetworkAdapterName VMHost = $this.VMHost VirtualSwitch = $distributedSwitch PortGroup = $portGroupName VMKernel = $true ErrorAction = 'SilentlyContinue' Verbose = $false } $vmKernelNetworkAdapter = Get-VMHostNetworkAdapter @getVMHostNetworkAdapterParams if ($null -eq $vmKernelNetworkAdapter) { return $true } } } else { for ($i = 0; $i -lt $this.VMKernelNicNames.Length; $i++) { $vmKernelNetworkAdapterName = $this.VMKernelNicNames[$i] $portGroupName = $this.PortGroupNames[$i] $getVMHostNetworkAdapterParams = @{ Server = $this.Connection Name = $vmKernelNetworkAdapterName VMHost = $this.VMHost VirtualSwitch = $distributedSwitch PortGroup = $portGroupName VMKernel = $true ErrorAction = 'SilentlyContinue' Verbose = $false } $vmKernelNetworkAdapter = Get-VMHostNetworkAdapter @getVMHostNetworkAdapterParams if ($null -eq $vmKernelNetworkAdapter) { return $true } } } return $false } <# .DESCRIPTION Ensures that the specified Distributed Port Groups exist. If a Distributed Port Group is specified and does not exist, it is created on the specified Distributed Switch. #> [array] EnsureDistributedPortGroupsExist($distributedSwitch, $vmKernelNetworkAdapters) { $portGroups = @() foreach ($distributedPortGroupName in $this.PortGroupNames) { $getVDPortGroupParams = @{ Server = $this.Connection Name = $distributedPortGroupName VDSwitch = $distributedSwitch ErrorAction = 'SilentlyContinue' Verbose = $false } $distributedPortGroup = Get-VDPortgroup @getVDPortGroupParams if ($null -eq $distributedPortGroup) { $distributedPortGroup = $this.CreateDistributedPortGroup($distributedPortGroupName, $distributedSwitch, $vmKernelNetworkAdapters) } $portGroups += $distributedPortGroup } return $portGroups } <# .DESCRIPTION Ensures that the passed VMKernel Network Adapter names and Port Group names count meets the following criteria: If at least one VMKernel Network Adapter is specified, one of the following requirements should be met: 1. The number of specified Port Groups should be equal to the number of specified VMKernel Network Adapters. 2. Only one Port Group is passed. If no VMKernel Network Adapter names are passed, no Port Group names should be passed as well. #> [void] EnsureVMKernelNetworkAdapterAndPortGroupNamesCountIsCorrect() { if ($this.VMkernelNicNames.Length -gt 0) { if ($this.PortGroupNames.Length -eq 0 -or ($this.VMkernelNicNames.Length -ne $this.PortGroupNames.Length -and $this.PortGroupNames.Length -ne 1)) { throw "$($this.VMKernelNicNames.Length) VMKernel Network Adapters specified and $($this.PortGroupNames.Length) Port Groups specified which is not valid." } } else { if ($this.PortGroupNames.Length -ne 0) { throw "$($this.PortGroupNames.Length) Port Groups specified and no VMKernel Network Adapters specified which is not valid." } } } <# .DESCRIPTION Creates a new Distributed Port Group with the specified name on the specified Distributed Switch. If a Standard Port Group with the same name exists on the Standard Switch from which the VMKernel Network Adapters are migrated and the Standard Port Group is associated with a VLAN, the new Distributed Port Group is created with the same VLAN to avoid any network misconfigurations due to VLANs not configured correctly. #> [PSObject] CreateDistributedPortGroup($distributedPortGroupName, $distributedSwitch, $vmKernelNetworkAdapters) { try { $createVDPortGroupMessageEnd = "." $newVDPortGroupParams = @{ Server = $this.Connection Name = $distributedPortGroupName VDSwitch = $distributedSwitch Confirm = $false ErrorAction = 'Stop' Verbose = $false } $vmKernelNetworkAdapterOfStandardPortGroup = $vmKernelNetworkAdapters | Where-Object -FilterScript { $_.PortGroupName -eq $distributedPortGroupName } if ($null -ne $vmKernelNetworkAdapterOfStandardPortGroup) { $standardPortGroup = $this.GetStandardPortGroup($distributedPortGroupName) # The VLAN for Distributed Port Groups is valid only in the specified range. if ($standardPortGroup.VLanId -In 1..4094) { $newVDPortGroupParams.VlanId = $standardPortGroup.VLanId $createVDPortGroupMessageEnd = " with VLAN ID $($standardPortGroup.VLanId)." } } $this.WriteLogUtil('Verbose', $this.CreateVDPortGroupMessage, @($distributedPortGroupName, $distributedSwitch.Name, $createVDPortGroupMessageEnd)) return New-VDPortgroup @newVDPortGroupParams } catch { throw ($this.CouldNotCreateVDPortGroupMessage -f $distributedPortGroupName, $distributedSwitch.Name, $_.Exception.Message) } } <# .DESCRIPTION Adds the VMHost to the specified Distributed Switch. #> [void] AddVMHostToDistributedSwitch($distributedSwitch) { try { $this.WriteLogUtil('Verbose', $this.AddVDSwitchToVMHostMessage, @($distributedSwitch.Name, $this.VMHost.Name)) $addVDSwitchVMHostParams = @{ Server = $this.Connection VDSwitch = $distributedSwitch VMHost = $this.VMHost Confirm = $false ErrorAction = 'Stop' Verbose = $false } Add-VDSwitchVMHost @addVDSwitchVMHostParams } catch { throw ($this.CouldNotAddVDSwitchToVMHostMessage -f $distributedSwitch.Name, $this.VMHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Adds the Physical Network Adapters to the specified Distributed Switch. #> [void] AddPhysicalNetworkAdaptersToDistributedSwitch($physicalNetworkAdapters, $distributedSwitch) { if ($null -eq $this.MigratePhysicalNicsOnly -or !$this.MigratePhysicalNicsOnly) { $this.WriteLogUtil('Warning', $this.MigratePhysicalNicsOnlyNotSpecified) return } try { $this.WriteLogUtil('Verbose', $this.AddPhysicalNicsToVDSwitchMessage, @( ($physicalNetworkAdapters.Name -Join ', '), $distributedSwitch.Name )) $addVDSwitchPhysicalNetworkAdapterParams = $this.GetAddVDSwitchPhysicalNetworkAdapterParams($distributedSwitch, $physicalNetworkAdapters) Add-VDSwitchPhysicalNetworkAdapter @addVDSwitchPhysicalNetworkAdapterParams } catch { throw ( $this.CouldNotAddPhysicalNicsToVDSwitchMessage -f @( ($physicalNetworkAdapters.Name -Join ', '), $distributedSwitch.Name, $_.Exception.Message ) ) } } <# .DESCRIPTION Adds the Physical Network Adapters and VMKernel Network Adapters to the specified Distributed Switch. #> [void] AddPhysicalNetworkAdaptersAndVMKernelNetworkAdaptersToDistributedSwitch($physicalNetworkAdapters, $vmKernelNetworkAdapters, $distributedSwitch) { $portGroups = $this.EnsureDistributedPortGroupsExist($distributedSwitch, $vmKernelNetworkAdapters) try { $this.WriteLogUtil('Verbose', $this.AddPhysicalNicsAndVMKernelNicsToVDSwitchMessage, @( ($physicalNetworkAdapters.Name -Join ', '), ($vmKernelNetworkAdapters.Name -Join ', '), $distributedSwitch.Name )) $addVDSwitchPhysicalNetworkAdapterParams = $this.GetAddVDSwitchPhysicalNetworkAdapterParams( $distributedSwitch, $physicalNetworkAdapters, $vmKernelNetworkAdapters, $portGroups ) Add-VDSwitchPhysicalNetworkAdapter @addVDSwitchPhysicalNetworkAdapterParams } catch { throw ( $this.CouldNotAddPhysicalNicsAndVMKernelNicsToVDSwitchMessage -f @( ($physicalNetworkAdapters.Name -Join ', '), ($vmKernelNetworkAdapters.Name -Join ', '), $distributedSwitch.Name, $_.Exception.Message ) ) } } <# .DESCRIPTION Populates the result returned from the Get() method. #> [void] PopulateResult($distributedSwitch, $result) { $result.Server = $this.Connection.Name $result.VMHostName = $this.VMHost.Name $result.VdsName = $distributedSwitch.Name if ($null -eq $distributedSwitch.ExtensionData.Config.Host.Config.Backing.PnicSpec) { $result.PhysicalNicNames = @() } else { $result.PhysicalNicNames = [string[]]($distributedSwitch.ExtensionData.Config.Host.Config.Backing.PnicSpec | Select-Object -ExpandProperty PNicDevice) } $result.VMkernelNicNames = @() $result.PortGroupNames = @() if ($this.VMKernelNicNames.Length -eq 0) { return } $getVMHostNetworkAdapterParams = @{ Server = $this.Connection VMHost = $this.VMHost VirtualSwitch = $distributedSwitch VMKernel = $true ErrorAction = 'SilentlyContinue' Verbose = $false } $vmKernelNetworkAdapters = Get-VMHostNetworkAdapter @getVMHostNetworkAdapterParams | Where-Object -FilterScript { $this.VMKernelNicNames.Contains($_.Name) } foreach ($vmKernelNetworkAdapter in $vmKernelNetworkAdapters) { $result.VMkernelNicNames += $vmKernelNetworkAdapter.Name $result.PortGroupNames += $vmKernelNetworkAdapter.PortGroupName } } } [DscResource()] class VMHostVssMigration : VMHostNetworkMigrationBaseDSC { <# .DESCRIPTION Specifies the name of the Standard Switch to which the passed Physical Network Adapters, VMKernel Network Adapters and Port Groups should be part of. #> [DscProperty(Key)] [string] $VssName <# .DESCRIPTION Specifies the names of the Port Groups to which the specified VMKernel Network Adapters should be attached. Accepts the same number of Port Groups as the number of VMKernel Network Adapters specified. The first Adapter is attached to the first Port Group, the second Adapter to the second Port Group, and so on. #> [DscProperty()] [string[]] $PortGroupNames [void] Set() { try { $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $this.RetrieveVMHost() $standardSwitch = $this.GetStandardSwitch() $physicalNetworkAdapters = $this.GetPhysicalNetworkAdapters() if ($this.VMkernelNicNames.Length -eq 0) { $this.AddPhysicalNetworkAdaptersToStandardSwitch($physicalNetworkAdapters, $standardSwitch) } else { $vmKernelNetworkAdapters = $this.GetVMKernelNetworkAdapters() $this.EnsureVMKernelNetworkAdapterAndPortGroupNamesCountIsCorrect() $this.AddPhysicalNetworkAdaptersAndVMKernelNetworkAdaptersToStandardSwitch($physicalNetworkAdapters, $vmKernelNetworkAdapters, $standardSwitch) } } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $this.RetrieveVMHost() $standardSwitch = $this.GetStandardSwitch() if ($this.ShouldAddPhysicalNetworkAdaptersToStandardSwitch($standardSwitch)) { return $false } if ($this.VMkernelNicNames.Length -eq 0 -and $this.PortGroupNames.Length -eq 0) { return $true } else { return !$this.ShouldAddVMKernelNetworkAdaptersAndPortGroupsToStandardSwitch($standardSwitch) } } finally { $this.DisconnectVIServer() } } [VMHostVssMigration] Get() { try { $result = [VMHostVssMigration]::new() $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $this.RetrieveVMHost() $standardSwitch = $this.GetStandardSwitch() $this.PopulateResult($standardSwitch, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the Standard Switch with the specified name from the specified VMHost if it exists. Otherwise it throws an exception. #> [PSObject] GetStandardSwitch() { try { $standardSwitch = Get-VirtualSwitch -Server $this.Connection -Name $this.VssName -VMHost $this.VMHost -Standard -ErrorAction Stop return $standardSwitch } catch { throw "Could not retrieve Standard Switch $($this.VssName). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Checks if all passed Physical Network Adapters are added to the Standard Switch. #> [bool] ShouldAddPhysicalNetworkAdaptersToStandardSwitch($standardSwitch) { $physicalNetworkAdapters = $this.GetPhysicalNetworkAdapters() if ($physicalNetworkAdapters.Length -eq 0) { throw 'At least one Physical Network Adapter needs to be specified.' } if ($null -eq $standardSwitch.Nic) { # No Physical Network Adapters are added to the Standard Switch. return $true } foreach ($physicalNetworkAdapter in $physicalNetworkAdapters) { $addedPhysicalNetworkAdapter = $standardSwitch.Nic | Where-Object -FilterScript { $_ -eq $physicalNetworkAdapter.Name } if ($null -eq $addedPhysicalNetworkAdapter) { return $true } } return $false } <# .DESCRIPTION Checks if all passed VMKernel Network Adapters and Port Groups are added to the Standard Switch. #> [bool] ShouldAddVMKernelNetworkAdaptersAndPortGroupsToStandardSwitch($standardSwitch) { $this.EnsureVMKernelNetworkAdapterAndPortGroupNamesCountIsCorrect() for ($i = 0; $i -lt $this.VMKernelNicNames.Length; $i++) { $vmKernelNetworkAdapterName = $this.VMKernelNicNames[$i] $getVMHostNetworkAdapterParams = @{ Server = $this.Connection Name = $vmKernelNetworkAdapterName VMHost = $this.VMHost VirtualSwitch = $standardSwitch VMKernel = $true ErrorAction = 'SilentlyContinue' } if ($this.PortGroupNames.Length -gt 0) { $getVMHostNetworkAdapterParams.PortGroup = $this.PortGroupNames[$i] } $vmKernelNetworkAdapter = Get-VMHostNetworkAdapter @getVMHostNetworkAdapterParams if ($null -eq $vmKernelNetworkAdapter) { return $true } } return $false } <# .DESCRIPTION Ensures that the passed VMKernel Network Adapter names and Port Group names count meets the following criteria: If VMKernel Network Adapter names are passed, the following requirements should be met: No Port Group names are passed or the number of Port Group names is the same as the number of VMKernel Network Adapter names. If no VMKernel Network Adapter names are passed, no Port Group names should be passed as well. #> [void] EnsureVMKernelNetworkAdapterAndPortGroupNamesCountIsCorrect() { if ($this.VMKernelNicNames.Length -gt 0) { if ($this.PortGroupNames.Length -gt 0 -and $this.VMkernelNicNames.Length -ne $this.PortGroupNames.Length) { throw "$($this.VMKernelNicNames.Length) VMKernel Network Adapters specified and $($this.PortGroupNames.Length) Port Groups specified which is not valid." } } else { if ($this.PortGroupNames.Length -gt 0) { throw "$($this.VMKernelNicNames.Length) VMKernel Network Adapters specified and $($this.PortGroupNames.Length) Port Groups specified which is not valid." } } } <# .DESCRIPTION Ensures that the specified Standard Port Groups exist. If a Standard Port Group is specified and does not exist, it is created on the specified Standard Switch. #> [void] EnsureStandardPortGroupsExist($standardSwitch) { foreach ($standardPortGroupName in $this.PortGroupNames) { $standardPortGroup = Get-VirtualPortGroup -Server $this.Connection -Name $standardPortGroupName -VirtualSwitch $standardSwitch -Standard -ErrorAction SilentlyContinue if ($null -eq $standardPortGroup) { try { New-VirtualPortGroup -Server $this.Connection -Name $standardPortGroupName -VirtualSwitch $standardSwitch -Confirm:$false -ErrorAction Stop } catch { throw "Cannot create Standard Port Group $standardPortGroupName on Standard Switch $($standardSwitch.Name). For more information: $($_.Exception.Message)" } } } } <# .DESCRIPTION Adds the Physical Network Adapters to the specified Standard Switch. #> [void] AddPhysicalNetworkAdaptersToStandardSwitch($physicalNetworkAdapters, $standardSwitch) { try { $addVirtualSwitchPhysicalNetworkAdapterParams = @{ Server = $this.Connection VirtualSwitch = $standardSwitch VMHostPhysicalNic = $physicalNetworkAdapters Confirm = $false ErrorAction = 'Stop' } Add-VirtualSwitchPhysicalNetworkAdapter @addVirtualSwitchPhysicalNetworkAdapterParams } catch { throw "Could not migrate Physical Network Adapters $($physicalNetworkAdapters) to Standard Switch $($standardSwitch.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Adds the Physical Network Adapters and VMKernel Network Adapters to the specified Standard Switch. #> [void] AddPhysicalNetworkAdaptersAndVMKernelNetworkAdaptersToStandardSwitch($physicalNetworkAdapters, $vmKernelNetworkAdapters, $standardSwitch) { $this.EnsureStandardPortGroupsExist($standardSwitch) try { $addVirtualSwitchPhysicalNetworkAdapterParams = @{ Server = $this.Connection VirtualSwitch = $standardSwitch VMHostPhysicalNic = $physicalNetworkAdapters VMHostVirtualNic = $vmKernelNetworkAdapters Confirm = $false ErrorAction = 'Stop' } if ($this.PortGroupNames.Length -gt 0) { $addVirtualSwitchPhysicalNetworkAdapterParams.VirtualNicPortgroup = $this.PortGroupNames } Add-VirtualSwitchPhysicalNetworkAdapter @addVirtualSwitchPhysicalNetworkAdapterParams } catch { throw "Could not migrate Physical Network Adapters $($physicalNetworkAdapters) and VMKernel Network Adapters $($vmKernelNetworkAdapters) to Standard Switch $($standardSwitch.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method. #> [void] PopulateResult($standardSwitch, $result) { $result.Server = $this.Connection.Name $result.VMHostName = $this.VMHost.Name $result.VssName = $standardSwitch.Name $result.PhysicalNicNames = $standardSwitch.Nic $result.VMkernelNicNames = @() $result.PortGroupNames = @() if ($this.VMKernelNicNames.Length -eq 0) { return } $vmKernelNetworkAdapters = Get-VMHostNetworkAdapter -Server $this.Connection -VMHost $this.VMHost -VirtualSwitch $standardSwitch -VMKernel -ErrorAction SilentlyContinue | Where-Object -FilterScript { $this.VMKernelNicNames.Contains($_.Name) } foreach ($vmKernelNetworkAdapter in $vmKernelNetworkAdapters) { $result.VMkernelNicNames += $vmKernelNetworkAdapter.Name $result.PortGroupNames += $vmKernelNetworkAdapter.PortGroupName } } } [DscResource()] class VMHostPhysicalNic : VMHostEntityBaseDSC { <# .DESCRIPTION Specifies the Name of the Physical Network Adapter which is going to be configured. #> [DscProperty(Key)] [string] $Name <# .DESCRIPTION Indicates whether the link is capable of full-duplex. The valid values are Full, Half and Unset. #> [DscProperty()] [Duplex] $Duplex = [Duplex]::Unset <# .DESCRIPTION Specifies the bit rate of the link. #> [DscProperty()] [nullable[int]] $BitRatePerSecMb <# .DESCRIPTION Indicates that the host network adapter speed/duplex settings are configured automatically. If the property is passed, the Duplex and BitRatePerSecMb properties will be ignored. #> [DscProperty()] [nullable[bool]] $AutoNegotiate [void] Set() { try { $this.ConnectVIServer() $this.RetrieveVMHost() $physicalNetworkAdapter = $this.GetPhysicalNetworkAdapter() $this.UpdatePhysicalNetworkAdapter($physicalNetworkAdapter) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $this.RetrieveVMHost() $physicalNetworkAdapter = $this.GetPhysicalNetworkAdapter() return !$this.ShouldUpdatePhysicalNetworkAdapter($physicalNetworkAdapter) } finally { $this.DisconnectVIServer() } } [VMHostPhysicalNic] Get() { try { $result = [VMHostPhysicalNic]::new() $result.Server = $this.Server $this.ConnectVIServer() $this.RetrieveVMHost() $physicalNetworkAdapter = $this.GetPhysicalNetworkAdapter() $result.VMHostName = $this.VMHost.Name $result.Name = $physicalNetworkAdapter.Name $this.PopulateResult($physicalNetworkAdapter, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the Physical Network Adapter with the specified name from the server if it exists. The Network Adapter must be a Physical Network Adapter. If the Physical Network Adapter does not exist, it throws an exception. #> [PSObject] GetPhysicalNetworkAdapter() { try { $physicalNetworkAdapter = Get-VMHostNetworkAdapter -Server $this.Connection -Name $this.Name -VMHost $this.VMHost -Physical -ErrorAction Stop return $physicalNetworkAdapter } catch { throw "Could not retrieve Physical Network Adapter $($this.Name) of VMHost $($this.VMHost.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Checks if the Physical Network Adapter should be updated. #> [bool] ShouldUpdatePhysicalNetworkAdapter($physicalNetworkAdapter) { $shouldUpdatePhysicalNetworkAdapter = @( $this.ShouldUpdateDscResourceSetting('BitRatePerSecMb', $physicalNetworkAdapter.BitRatePerSec, $this.BitRatePerSecMb) ) <# The Duplex value on the server is stored as boolean indicating if the link is capable of full-duplex. So mapping between the enum and boolean values needs to be performed for comparison purposes. #> if ($this.Duplex -ne [Duplex]::Unset) { if ($physicalNetworkAdapter.FullDuplex) { $shouldUpdatePhysicalNetworkAdapter += ($this.Duplex -ne [Duplex]::Full) } else { $shouldUpdatePhysicalNetworkAdapter += ($this.Duplex -ne [Duplex]::Half) } } <# If the network adapter speed/duplex settings are configured automatically, the Link Speed property is $null on the server. #> if ($null -ne $this.AutoNegotiate) { if ($this.AutoNegotiate) { $shouldUpdatePhysicalNetworkAdapter += ($null -ne $physicalNetworkAdapter.ExtensionData.Spec.LinkSpeed) } else { $shouldUpdatePhysicalNetworkAdapter += ($null -eq $physicalNetworkAdapter.ExtensionData.Spec.LinkSpeed) } } return ($shouldUpdatePhysicalNetworkAdapter -Contains $true) } <# .DESCRIPTION Performs an update operation on the specified Physical Network Adapter. #> [void] UpdatePhysicalNetworkAdapter($physicalNetworkAdapter) { $physicalNetworkAdapterParams = @{} $physicalNetworkAdapterParams.PhysicalNic = $physicalNetworkAdapter $physicalNetworkAdapterParams.Confirm = $false $physicalNetworkAdapterParams.ErrorAction = 'Stop' if ($null -ne $this.AutoNegotiate -and $this.AutoNegotiate) { $physicalNetworkAdapterParams.AutoNegotiate = $this.AutoNegotiate } else { if ($this.Duplex -ne [Duplex]::Unset) { $physicalNetworkAdapterParams.Duplex = $this.Duplex.ToString() } if ($null -ne $this.BitRatePerSecMb) { $physicalNetworkAdapterParams.BitRatePerSecMb = $this.BitRatePerSecMb } } try { Set-VMHostNetworkAdapter @physicalNetworkAdapterParams } catch { throw "Cannot update Physical Network Adapter $($physicalNetworkAdapter.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Physical Network Adapter from the server. #> [void] PopulateResult($physicalNetworkAdapter, $result) { <# AutoNegotiate property is not present on the server, so it should be populated with the value provided by user. #> $result.AutoNegotiate = $this.AutoNegotiate $result.BitRatePerSecMb = $physicalNetworkAdapter.BitRatePerSec if ($physicalNetworkAdapter.FullDuplex) { $result.Duplex = [Duplex]::Full } else { $result.Duplex = [Duplex]::Half } } } [DscResource()] class VMHostVdsNic : VMHostNicBaseDSC { <# .DESCRIPTION Specifies the name of the VMKernel NIC connected to the specified Distributed Port Group on the specified VDSwitch. #> [DscProperty(Key)] [string] $Name <# .DESCRIPTION Specifies the name of the VDSwitch to which the VMKernel NIC is added. #> [DscProperty(Key)] [string] $VdsName <# .DESCRIPTION Specifies the instance of the 'InventoryUtil' class that is used for Inventory operations. #> hidden [InventoryUtil] $InventoryUtil hidden [string] $VMHostIsNotAddedToVDSwitchMessage = "VMHost {0} should be added to VDSwitch {1} before configuring the VMKernel NIC." hidden [string] $CouldNotFindVMKernelNICMessage = "VMKernel NIC {0} connected to Distributed Port Group {1} on VDSwitch {2} could not be found." [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $this.InitInventoryUtil() $this.RetrieveVMHost() $vdSwitch = $this.InventoryUtil.GetVDSwitch($this.VdsName) $this.EnsureVMHostIsAddedToTheVDSwitch($vdSwitch) $vmKernelNic = $this.GetVMKernelNic($vdSwitch) if ($this.Ensure -eq [Ensure]::Present) { $this.UpdateVMHostNetworkAdapter($vmKernelNic) } else { if ($null -ne $vmKernelNic) { $this.RemoveVMHostNetworkAdapter($vmKernelNic) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $this.EnsureConnectionIsvCenter() $this.InitInventoryUtil() $this.RetrieveVMHost() $vdSwitch = $this.InventoryUtil.GetVDSwitch($this.VdsName) $this.EnsureVMHostIsAddedToTheVDSwitch($vdSwitch) $vmKernelNic = $this.GetVMKernelNic($vdSwitch) $result = $null if ($this.Ensure -eq [Ensure]::Present) { $result = !$this.ShouldUpdateVMHostNetworkAdapter($vmKernelNic) } else { $result = ($null -eq $vmKernelNic) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostVdsNic] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostVdsNic]::new() $this.EnsureConnectionIsvCenter() $this.InitInventoryUtil() $this.RetrieveVMHost() $vdSwitch = $this.InventoryUtil.GetVDSwitch($this.VdsName) $this.EnsureVMHostIsAddedToTheVDSwitch($vdSwitch) $vmKernelNic = $this.GetVMKernelNic($vdSwitch) if ($null -eq $vmKernelNic) { $result.VdsName = $this.VdsName $result.Name = $this.Name } else { $result.VdsName = $vdSwitch.Name $result.Name = $vmKernelNic.Name } $this.PopulateResult($vmKernelNic, $result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Initializes an instance of the 'InventoryUtil' class. #> [void] InitInventoryUtil() { if ($null -eq $this.InventoryUtil) { $this.InventoryUtil = [InventoryUtil]::new($this.Connection, $this.Ensure) } } <# .DESCRIPTION Retrieves the VMKernel NIC connected to the specified Distributed Port Group and added to the specified VDSwitch from the server if it exists, otherwise the method returns $null. #> [PSObject] GetVMKernelNic($vdSwitch) { if ($null -eq $vdSwitch) { <# If the VDSwitch is $null, it means that Ensure was set to 'Absent' and the VMKernel NIC is not added to the specified VDSwitch. #> return $null } $getVMHostNetworkAdapterParams = @{ Server = $this.Connection VMHost = $this.VMHost Name = $this.Name VirtualSwitch = $vdSwitch PortGroup = $this.PortGroupName VMKernel = $true ErrorAction = 'SilentlyContinue' Verbose = $false } $vmKernelNic = Get-VMHostNetworkAdapter @getVMHostNetworkAdapterParams if ($this.Ensure -eq [Ensure]::Present -and $null -eq $vmKernelNic) { throw ($this.CouldNotFindVMKernelNICMessage -f $this.Name, $this.PortGroupName, $vdSwitch.Name) } return $vmKernelNic } <# .DESCRIPTION Checks if the specified VMHost is part of the specified VDSwitch and if not, throws an exception. #> [void] EnsureVMHostIsAddedToTheVDSwitch($vdSwitch) { if ($null -eq $vdSwitch) { <# If the VDSwitch is $null, it means that Ensure was set to 'Absent' and the VMKernel NIC does not exist for the specified VDSwitch. So there is no need to ensure that the VMHost is part of the VDSwitch. #> return } $whereObjectParams = @{ FilterScript = { $_.DvsName -eq $vdSwitch.Name } } $addedVMHost = $this.VMHost.ExtensionData.Config.Network.ProxySwitch | Where-Object @whereObjectParams if ($null -eq $addedVMHost) { throw ($this.VMHostIsNotAddedToVDSwitchMessage -f $this.VMHost.Name, $vdSwitch.Name) } } } [DscResource()] class VMHostVssNic : VMHostNicBaseDSC { <# .DESCRIPTION Specifies the name of the Virtual Switch to which the VMKernel Network Adapter should be connected. #> [DscProperty(Key)] [string] $VssName [void] Set() { try { $this.ConnectVIServer() $this.RetrieveVMHost() $virtualSwitch = $this.GetVirtualSwitch() $vmHostNetworkAdapter = $this.GetVMHostNetworkAdapter($virtualSwitch) if ($null -ne $vmHostNetworkAdapter) { <# Here the retrieval of the VMKernel is done for the second time because retrieving it by Virtual Switch and Port Group produces errors when trying to update or delete it. The errors do not occur when the retrieval is done by Name. #> $vmHostNetworkAdapter = Get-VMHostNetworkAdapter -Server $this.Connection -Name $vmHostNetworkAdapter.Name -VMHost $this.VMHost -VMKernel } if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $vmHostNetworkAdapter) { $vmHostNetworkAdapter = $this.AddVMHostNetworkAdapter($virtualSwitch, $null) } if ($this.ShouldUpdateVMHostNetworkAdapter($vmHostNetworkAdapter)) { $this.UpdateVMHostNetworkAdapter($vmHostNetworkAdapter) } } else { if ($null -ne $vmHostNetworkAdapter) { $this.RemoveVMHostNetworkAdapter($vmHostNetworkAdapter) } } } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $this.RetrieveVMHost() $virtualSwitch = $this.GetVirtualSwitch() $vmHostNetworkAdapter = $this.GetVMHostNetworkAdapter($virtualSwitch) $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $vmHostNetworkAdapter) { $result = $false } else { $result = !$this.ShouldUpdateVMHostNetworkAdapter($vmHostNetworkAdapter) } } else { $result = ($null -eq $vmHostNetworkAdapter) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostVssNic] Get() { try { $result = [VMHostVssNic]::new() $this.ConnectVIServer() $this.RetrieveVMHost() $virtualSwitch = $this.GetVirtualSwitch() $vmHostNetworkAdapter = $this.GetVMHostNetworkAdapter($virtualSwitch) $result.VssName = $virtualSwitch.Name $this.PopulateResult($vmHostNetworkAdapter, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the Virtual Switch with the specified name from the server if it exists. The Virtual Switch must be a Standard Virtual Switch. If the Virtual Switch does not exist and Ensure is set to 'Absent', $null is returned. Otherwise it throws an exception. #> [PSObject] GetVirtualSwitch() { if ($this.Ensure -eq [Ensure]::Absent) { return Get-VirtualSwitch -Server $this.Connection -Name $this.VssName -VMHost $this.VMHost -Standard -ErrorAction SilentlyContinue } else { try { $virtualSwitch = Get-VirtualSwitch -Server $this.Connection -Name $this.VssName -VMHost $this.VMHost -Standard -ErrorAction Stop return $virtualSwitch } catch { throw "Could not retrieve Virtual Switch $($this.VssName) of VMHost $($this.VMHost.Name). For more information: $($_.Exception.Message)" } } } } [DscResource()] class VMHostIPRoute : VMHostBaseDSC { <# .DESCRIPTION Specifies the gateway IPv4/IPv6 address of the route. #> [DscProperty(Key)] [string] $Gateway <# .DESCRIPTION Specifies the destination IPv4/IPv6 address of the route. #> [DscProperty(Key)] [string] $Destination <# .DESCRIPTION Specifies the prefix length of the destination IP address. For IPv4, the valid values are from 0 to 32, and for IPv6 - from 0 to 128. #> [DscProperty(Key)] [int] $PrefixLength <# .DESCRIPTION Specifies whether the IPv4/IPv6 route should be present or absent. #> [DscProperty(Mandatory = $true)] [Ensure] $Ensure hidden [string] $CreateVMHostIPRouteMessage = "Creating IP Route with Gateway address {0} and Destination address {1} on VMHost {2}." hidden [string] $RemoveVMHostIPRouteMessage = "Removing IP Route with Gateway address {0} and Destination address {1} on VMHost {2}." hidden [string] $CouldNotCreateVMHostIPRouteMessage = "Could not create IP Route with Gateway address {0} and Destination address {1} on VMHost {2}. For more information: {3}" hidden [string] $CouldNotRemoveVMHostIPRouteMessage = "Could not remove IP Route with Gateway address {0} and Destination address {1} on VMHost {2}. For more information: {3}" [void] Set() { try { $this.WriteLogUtil('Verbose', $this.SetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostIPRoute = $this.GetVMHostIPRoute($vmHost) if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $vmHostIPRoute) { $this.NewVMHostIPRoute($vmHost) } } else { if ($null -ne $vmHostIPRoute) { $this.RemoveVMHostIPRoute($vmHostIPRoute) } } } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.SetMethodEndMessage, @($this.DscResourceName)) } } [bool] Test() { try { $this.WriteLogUtil('Verbose', $this.TestMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostIPRoute = $this.GetVMHostIPRoute($vmHost) $result = $null if ($this.Ensure -eq [Ensure]::Present) { $result = ($null -ne $vmHostIPRoute) } else { $result = ($null -eq $vmHostIPRoute) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.TestMethodEndMessage, @($this.DscResourceName)) } } [VMHostIPRoute] Get() { try { $this.WriteLogUtil('Verbose', $this.GetMethodStartMessage, @($this.DscResourceName)) $this.ConnectVIServer() $result = [VMHostIPRoute]::new() $vmHost = $this.GetVMHost() $vmHostIPRoute = $this.GetVMHostIPRoute($vmHost) $this.PopulateResult($result, $vmHostIPRoute) return $result } finally { $this.DisconnectVIServer() $this.WriteLogUtil('Verbose', $this.GetMethodEndMessage, @($this.DscResourceName)) } } <# .DESCRIPTION Retrieves the configured IPv4/IPv6 route with the specified Gateway and Destination addresses if it exists. #> [PSObject] GetVMHostIPRoute($vmHost) { return (Get-VMHostRoute -Server $this.Connection -VMHost $vmHost -ErrorAction SilentlyContinue -Verbose:$false | Where-Object -FilterScript { $_.Gateway -eq $this.Gateway -and $_.Destination -eq $this.Destination -and $_.PrefixLength -eq $this.PrefixLength }) } <# .DESCRIPTION Creates a new IP route with the specified Gateway and Destination addresses. #> [void] NewVMHostIPRoute($vmHost) { $newVMHostRouteParams = @{ Server = $this.Connection VMHost = $vmHost Gateway = $this.Gateway Destination = $this.Destination PrefixLength = $this.PrefixLength Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.CreateVMHostIPRouteMessage, @($this.Gateway, $this.Destination, $vmHost.Name)) New-VMHostRoute @newVMHostRouteParams } catch { throw ($this.CouldNotCreateVMHostIPRouteMessage -f $this.Gateway, $this.Destination, $vmHost.Name, $_.Exception.Message) } } <# .DESCRIPTION Removes the IP route with the specified Gateway and Destination addresses. #> [void] RemoveVMHostIPRoute($vmHostIPRoute) { $removeVMHostRouteParams = @{ VMHostRoute = $vmHostIPRoute Confirm = $false ErrorAction = 'Stop' Verbose = $false } try { $this.WriteLogUtil('Verbose', $this.RemoveVMHostIPRouteMessage, @($this.Gateway, $this.Destination, $this.Name)) Remove-VMHostRoute @removeVMHostRouteParams } catch { throw ($this.CouldNotRemoveVMHostIPRouteMessage -f $this.Gateway, $this.Destination, $this.Name, $_.Exception.Message) } } <# .DESCRIPTION Populates the result returned from the Get method. #> [void] PopulateResult($result, $vmHostIPRoute) { $result.Server = $this.Connection.Name $result.Name = $this.Name if ($null -ne $vmHostIPRoute) { $result.Ensure = [Ensure]::Present $result.Gateway = $vmHostIPRoute.Gateway $result.Destination = $vmHostIPRoute.Destination $result.PrefixLength = $vmHostIPRoute.PrefixLength } else { $result.Ensure = [Ensure]::Absent $result.Gateway = $this.Gateway $result.Destination = $this.Destination $result.PrefixLength = $this.PrefixLength } } } [DscResource()] class VMHostGraphics : VMHostGraphicsBaseDSC { <# .DESCRIPTION Specifies the default graphics type for the specified VMHost. #> [DscProperty(Mandatory)] [GraphicsType] $GraphicsType <# .DESCRIPTION Specifies the policy for assigning shared passthrough VMs to a host graphics device. #> [DscProperty(Mandatory)] [SharedPassthruAssignmentPolicy] $SharedPassthruAssignmentPolicy [void] Set() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostGraphicsManager = $this.GetVMHostGraphicsManager($vmHost) $this.EnsureVMHostIsInMaintenanceMode($vmHost) $this.UpdateGraphicsConfiguration($vmHostGraphicsManager) $this.RestartVMHost($vmHost) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostGraphicsManager = $this.GetVMHostGraphicsManager($vmHost) return !$this.ShouldUpdateGraphicsConfiguration($vmHostGraphicsManager) } finally { $this.DisconnectVIServer() } } [VMHostGraphics] Get() { try { $result = [VMHostGraphics]::new() $result.Server = $this.Server $result.RestartTimeoutMinutes = $this.RestartTimeoutMinutes $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostGraphicsManager = $this.GetVMHostGraphicsManager($vmHost) $result.Name = $vmHost.Name $result.GraphicsType = $vmHostGraphicsManager.GraphicsConfig.HostDefaultGraphicsType $result.SharedPassthruAssignmentPolicy = $vmHostGraphicsManager.GraphicsConfig.SharedPassthruAssignmentPolicy return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Checks if the Graphics Configuration needs to be updated with the desired values. #> [bool] ShouldUpdateGraphicsConfiguration($vmHostGraphicsManager) { $shouldUpdateGraphicsConfiguration = @( $this.ShouldUpdateDscResourceSetting( 'GraphicsType', [string] $vmHostGraphicsManager.GraphicsConfig.HostDefaultGraphicsType, $this.GraphicsType.ToString() ), $this.ShouldUpdateDscResourceSetting( 'SharedPassthruAssignmentPolicy', [string] $vmHostGraphicsManager.GraphicsConfig.SharedPassthruAssignmentPolicy, $this.SharedPassthruAssignmentPolicy.ToString() ) ) return ($shouldUpdateGraphicsConfiguration -Contains $true) } <# .DESCRIPTION Performs an update on the Graphics Configuration of the specified VMHost. #> [void] UpdateGraphicsConfiguration($vmHostGraphicsManager) { $vmHostGraphicsConfig = New-Object VMware.Vim.HostGraphicsConfig $vmHostGraphicsConfig.HostDefaultGraphicsType = $this.ConvertEnumValueToServerValue($this.GraphicsType) $vmHostGraphicsConfig.SharedPassthruAssignmentPolicy = $this.ConvertEnumValueToServerValue($this.SharedPassthruAssignmentPolicy) try { Update-GraphicsConfig -VMHostGraphicsManager $vmHostGraphicsManager -VMHostGraphicsConfig $vmHostGraphicsConfig } catch { throw "The Graphics Configuration of VMHost $($this.Name) could not be updated: $($_.Exception.Message)" } } } [DscResource()] class VMHostGraphicsDevice : VMHostGraphicsBaseDSC { <# .DESCRIPTION Specifies the Graphics device identifier (ex. PCI ID). #> [DscProperty(Key)] [string] $Id <# .DESCRIPTION Specifies the graphics type for the specified Device in 'Id' property. #> [DscProperty(Mandatory)] [GraphicsType] $GraphicsType [void] Set() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostGraphicsManager = $this.GetVMHostGraphicsManager($vmHost) $this.EnsureVMHostIsInMaintenanceMode($vmHost) $this.UpdateGraphicsConfiguration($vmHostGraphicsManager) $this.RestartVMHost($vmHost) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostGraphicsManager = $this.GetVMHostGraphicsManager($vmHost) $foundDevice = $this.GetGraphicsDevice($vmHostGraphicsManager) $result = !$this.ShouldUpdateDscResourceSetting( 'GraphicsType', [string] $foundDevice.GraphicsType, $this.GraphicsType.ToString() ) $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostGraphicsDevice] Get() { try { $result = [VMHostGraphicsDevice]::new() $result.Server = $this.Server $result.RestartTimeoutMinutes = $this.RestartTimeoutMinutes $this.ConnectVIServer() $vmHost = $this.GetVMHost() $vmHostGraphicsManager = $this.GetVMHostGraphicsManager($vmHost) $foundDevice = $this.GetGraphicsDevice($vmHostGraphicsManager) $result.Name = $vmHost.Name $result.Id = $foundDevice.DeviceId $result.GraphicsType = $foundDevice.GraphicsType return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Retrieves the Graphics Device with the specified Id from the server. #> [PSObject] GetGraphicsDevice($vmHostGraphicsManager) { $foundDevice = $vmHostGraphicsManager.GraphicsConfig.DeviceType | Where-Object { $_.DeviceId -eq $this.Id } if ($null -eq $foundDevice) { throw "Device $($this.Id) was not found in the available Graphics devices." } return $foundDevice } <# .DESCRIPTION Performs an update on the Graphics Configuration of the specified VMHost by changing the Graphics Type for the specified Device. #> [void] UpdateGraphicsConfiguration($vmHostGraphicsManager) { $vmHostGraphicsConfig = New-Object VMware.Vim.HostGraphicsConfig $vmHostGraphicsConfig.HostDefaultGraphicsType = $vmHostGraphicsManager.GraphicsConfig.HostDefaultGraphicsType $vmHostGraphicsConfig.SharedPassthruAssignmentPolicy = $vmHostGraphicsManager.GraphicsConfig.SharedPassthruAssignmentPolicy $vmHostGraphicsConfig.DeviceType = @() $vmHostGraphicsConfigDeviceType = New-Object VMware.Vim.HostGraphicsConfigDeviceType $vmHostGraphicsConfigDeviceType.DeviceId = $this.Id $vmHostGraphicsConfigDeviceType.GraphicsType = $this.ConvertEnumValueToServerValue($this.GraphicsType) $vmHostGraphicsConfig.DeviceType += $vmHostGraphicsConfigDeviceType try { Update-GraphicsConfig -VMHostGraphicsManager $vmHostGraphicsManager -VMHostGraphicsConfig $vmHostGraphicsConfig } catch { throw "The Graphics Configuration of VMHost $($this.Name) could not be updated: $($_.Exception.Message)" } } } [DscResource()] class VMHostVss : VMHostVssBaseDSC { <# .DESCRIPTION The maximum transmission unit (MTU) associated with this virtual switch in bytes. #> [DscProperty()] [nullable[int]] $Mtu <# .DESCRIPTION The virtual switch key. #> [DscProperty(NotConfigurable)] [string] $Key <# .DESCRIPTION The number of ports that this virtual switch currently has. #> [DscProperty(NotConfigurable)] [int] $NumPorts <# .DESCRIPTION The number of ports that are available on this virtual switch. #> [DscProperty(NotConfigurable)] [int] $NumPortsAvailable <# .DESCRIPTION The set of physical network adapters associated with this bridge. #> [DscProperty(NotConfigurable)] [string[]] $Pnic <# .DESCRIPTION The list of port groups configured for this virtual switch. #> [DscProperty(NotConfigurable)] [string[]] $PortGroup [void] Set() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $this.UpdateVss($vmHost) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $vss = $this.GetVss() $result = $null if ($this.Ensure -eq [Ensure]::Present) { $result = ($null -ne $vss -and $this.Equals($vss)) } else { $result = ($null -eq $vss) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostVss] Get() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $result = [VMHostVss]::new() $result.Server = $this.Server $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $result.Name = $vmHost.Name $this.PopulateResult($vmHost, $result) $result.Ensure = if ([string]::Empty -ne $result.Key) { 'Present' } else { 'Absent' } return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Returns a boolean value indicating if the VMHostVss should be updated. #> [bool] Equals($vss) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vssTest = @( $this.ShouldUpdateDscResourceSetting('VssName', $vss.Name, $this.VssName), $this.ShouldUpdateDscResourceSetting('Mtu', $vss.Mtu, $this.Mtu) ) return ($vssTest -NotContains $true) } <# .DESCRIPTION Updates the configuration of the virtual switch. #> [void] UpdateVss($vmHost) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vssConfigArgs = @{ Name = $this.VssName Mtu = $this.Mtu } $vss = $this.GetVss() if ($this.Ensure -eq 'Present') { if ($null -ne $vss) { if ($this.Equals($vss)) { return } $vssConfigArgs.Add('Operation', 'edit') } else { $vssConfigArgs.Add('Operation', 'add') } } else { if ($null -eq $vss) { return } $vssConfigArgs.Add('Operation', 'remove') } try { Update-Network -NetworkSystem $this.vmHostNetworkSystem -VssConfig $vssConfigArgs -ErrorAction Stop } catch { throw "The Virtual Switch Config could not be updated: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the virtual switch. #> [void] PopulateResult($vmHost, $vmHostVSS) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $currentVss = $this.GetVss() if ($null -ne $currentVss) { $vmHostVSS.Key = $currentVss.Key $vmHostVSS.Mtu = $currentVss.Mtu $vmHostVSS.VssName = $currentVss.Name $vmHostVSS.NumPortsAvailable = $currentVss.NumPortsAvailable $vmHostVSS.Pnic = $currentVss.Pnic $vmHostVSS.PortGroup = $currentVss.PortGroup } else{ $vmHostVSS.Key = [string]::Empty $vmHostVSS.Mtu = $this.Mtu $vmHostVSS.VssName = $this.VssName } } } [DscResource()] class VMHostVssBridge : VMHostVssBaseDSC { <# .DESCRIPTION The list of keys of the physical network adapters to be bridged. #> [DscProperty()] [string[]] $NicDevice <# .DESCRIPTION The beacon configuration to probe for the validity of a link. If this is set, beacon probing is configured and will be used. If this is not set, beacon probing is disabled. Determines how often, in seconds, a beacon should be sent. #> [DscProperty()] [nullable[int]] $BeaconInterval <# .DESCRIPTION The link discovery protocol, whether to advertise or listen. #> [DscProperty()] [LinkDiscoveryProtocolOperation] $LinkDiscoveryProtocolOperation = [LinkDiscoveryProtocolOperation]::Unset <# .DESCRIPTION The link discovery protocol type. #> [DscProperty()] [LinkDiscoveryProtocolProtocol] $LinkDiscoveryProtocolProtocol = [LinkDiscoveryProtocolProtocol]::Unset [void] Set() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $this.UpdateVssBridge($vmHost) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $vss = $this.GetVss() $result = $null if ($this.Ensure -eq [Ensure]::Present) { $result = ($null -ne $vss -and $this.Equals($vss)) } else { $this.NicDevice = @() $this.BeaconInterval = 0 $this.LinkDiscoveryProtocolProtocol = [LinkDiscoveryProtocolProtocol]::Unset $result = ($null -eq $vss -or $this.Equals($vss)) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostVssBridge] Get() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $result = [VMHostVssBridge]::new() $result.Server = $this.Server $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $result.Name = $vmHost.Name $this.PopulateResult($vmHost, $result) $result.Ensure = if ([string]::Empty -ne $result.VssName) { 'Present' } else { 'Absent' } return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Returns a boolean value indicating if the VMHostVssBridge should to be updated. #> [bool] Equals($vss) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vssBridgeTest = @( $this.ShouldUpdateArraySetting('NicDevice', $vss.Spec.Bridge.NicDevice, $this.NicDevice), $this.ShouldUpdateDscResourceSetting('BeaconInterval', $vss.Spec.Bridge.Beacon.Interval, $this.BeaconInterval), $this.ShouldUpdateDscResourceSetting( 'LinkDiscoveryProtocolProtocol', [string] $vss.Spec.Bridge.LinkDiscoveryProtocolConfig.Protocol, $this.LinkDiscoveryProtocolProtocol.ToString() ), $this.ShouldUpdateDscResourceSetting( 'LinkDiscoveryProtocolOperation', [string] $vss.Spec.Bridge.LinkDiscoveryProtocolConfig.Operation, $this.LinkDiscoveryProtocolOperation.ToString() ) ) return ($vssBridgeTest -NotContains $true) } <# .DESCRIPTION Updates the Bridge configuration of the virtual switch. #> [void] UpdateVssBridge($vmHost) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vssBridgeArgs = @{ Name = $this.VssName NicDevice = $this.NicDevice } # The Bridge configuration of the Standard Switch should be populated only when the Nic devices are passed. if ($this.NicDevice.Count -gt 0) { if ($null -ne $this.BeaconInterval) { $vssBridgeArgs.BeaconInterval = $this.BeaconInterval } if ($this.LinkDiscoveryProtocolProtocol -ne [LinkDiscoveryProtocolProtocol]::Unset) { $vssBridgeArgs.Add('LinkDiscoveryProtocolProtocol', $this.LinkDiscoveryProtocolProtocol.ToString()) $vssBridgeArgs.Add('LinkDiscoveryProtocolOperation', $this.LinkDiscoveryProtocolOperation.ToString()) } } $vss = $this.GetVss() if ($this.Ensure -eq 'Present') { if ($this.Equals($vss)) { return } } else { $vssBridgeArgs.NicDevice = @() } $vssBridgeArgs.Add('Operation', 'edit') try { Update-Network -NetworkSystem $this.vmHostNetworkSystem -VssBridgeConfig $vssBridgeArgs -ErrorAction Stop } catch { throw "The Virtual Switch Bridge Config could not be updated: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Bridge settings of the Virtual Switch. #> [void] PopulateResult($vmHost, $vmHostVSSBridge) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $currentVss = $this.GetVss() if ($null -ne $currentVss) { $vmHostVSSBridge.VssName = $currentVss.Name $vmHostVSSBridge.NicDevice = $currentVss.Spec.Bridge.NicDevice $vmHostVSSBridge.BeaconInterval = $currentVss.Spec.Bridge.Beacon.Interval if ($null -ne $currentVss.Spec.Bridge.linkDiscoveryProtocolConfig) { $vmHostVSSBridge.LinkDiscoveryProtocolOperation = $currentVss.Spec.Bridge.LinkDiscoveryProtocolConfig.Operation.ToString() $vmHostVSSBridge.LinkDiscoveryProtocolProtocol = $currentVss.Spec.Bridge.LinkDiscoveryProtocolConfig.Protocol.ToString() } } else { $vmHostVSSBridge.VssName = $this.VssName $vmHostVSSBridge.NicDevice = $this.NicDevice $vmHostVSSBridge.BeaconInterval = $this.BeaconInterval $vmHostVSSBridge.LinkDiscoveryProtocolOperation = $this.LinkDiscoveryProtocolOperation $vmHostVSSBridge.LinkDiscoveryProtocolProtocol = $this.LinkDiscoveryProtocolProtocol } } } [DscResource()] class VMHostVssSecurity : VMHostVssBaseDSC { <# .DESCRIPTION The flag to indicate whether or not all traffic is seen on the port. #> [DscProperty()] [nullable[bool]] $AllowPromiscuous <# .DESCRIPTION The flag to indicate whether or not the virtual network adapter should be allowed to send network traffic with a different MAC address than that of the virtual network adapter. #> [DscProperty()] [nullable[bool]] $ForgedTransmits <# .DESCRIPTION The flag to indicate whether or not the Media Access Control (MAC) address can be changed. #> [DscProperty()] [nullable[bool]] $MacChanges [void] Set() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $this.UpdateVssSecurity($vmHost) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $vss = $this.GetVss() $result = $null if ($this.Ensure -eq [Ensure]::Present) { $result = ($null -ne $vss -and $this.Equals($vss)) } else { $this.AllowPromiscuous = $false $this.ForgedTransmits = $true $this.MacChanges = $true $result = ($null -eq $vss -or $this.Equals($vss)) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostVssSecurity] Get() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $result = [VMHostVssSecurity]::new() $result.Server = $this.Server $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $result.Name = $vmHost.Name $this.PopulateResult($vmHost, $result) $result.Ensure = if ([string]::Empty -ne $result.VssName) { 'Present' } else { 'Absent' } return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Returns a boolean value indicating if the VMHostVssSecurity should to be updated. #> [bool] Equals($vss) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vssSecurityTest = @( $this.ShouldUpdateDscResourceSetting('AllowPromiscuous', $vss.Spec.Policy.Security.AllowPromiscuous, $this.AllowPromiscuous), $this.ShouldUpdateDscResourceSetting('ForgedTransmits', $vss.Spec.Policy.Security.ForgedTransmits, $this.ForgedTransmits), $this.ShouldUpdateDscResourceSetting('MacChanges', $vss.Spec.Policy.Security.MacChanges, $this.MacChanges) ) return ($vssSecurityTest -NotContains $true) } <# .DESCRIPTION Updates the configuration of the virtual switch. #> [void] UpdateVssSecurity($vmHost) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vssSecurityArgs = @{ Name = $this.VssName AllowPromiscuous = $this.AllowPromiscuous ForgedTransmits = $this.ForgedTransmits MacChanges = $this.MacChanges } $vss = $this.GetVss() if ($this.Ensure -eq 'Present') { if ($this.Equals($vss)) { return } $vssSecurityArgs.Add('Operation', 'edit') } else { $vssSecurityArgs.AllowPromiscuous = $false $vssSecurityArgs.ForgedTransmits = $true $vssSecurityArgs.MacChanges = $true $vssSecurityArgs.Add('Operation', 'edit') } try { Update-Network -NetworkSystem $this.vmHostNetworkSystem -VssSecurityConfig $vssSecurityArgs -ErrorAction Stop } catch { throw "The Virtual Switch Security Config could not be updated: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Security settings of the Virtual Switch. #> [void] PopulateResult($vmHost, $vmHostVSSSecurity) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $currentVss = $this.GetVss() if ($null -ne $currentVss) { $vmHostVSSSecurity.VssName = $currentVss.Name $vmHostVSSSecurity.AllowPromiscuous = $currentVss.Spec.Policy.Security.AllowPromiscuous $vmHostVSSSecurity.ForgedTransmits = $currentVss.Spec.Policy.Security.ForgedTransmits $vmHostVSSSecurity.MacChanges = $currentVss.Spec.Policy.Security.MacChanges } else { $vmHostVSSSecurity.VssName = $this.VssName $vmHostVSSSecurity.AllowPromiscuous = $this.AllowPromiscuous $vmHostVSSSecurity.ForgedTransmits = $this.ForgedTransmits $vmHostVSSSecurity.MacChanges = $this.MacChanges } } } [DscResource()] class VMHostVssShaping : VMHostVssBaseDSC { <# .DESCRIPTION The average bandwidth in bits per second if shaping is enabled on the port. #> [DscProperty()] [nullable[long]] $AverageBandwidth <# .DESCRIPTION The maximum burst size allowed in bytes if shaping is enabled on the port. #> [DscProperty()] [nullable[long]] $BurstSize <# .DESCRIPTION The flag to indicate whether or not traffic shaper is enabled on the port. #> [DscProperty()] [nullable[bool]] $Enabled <# .DESCRIPTION The peak bandwidth during bursts in bits per second if traffic shaping is enabled on the port. #> [DscProperty()] [nullable[long]] $PeakBandwidth [void] Set() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $this.UpdateVssShaping($vmHost) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $vss = $this.GetVss() $result = $null if ($this.Ensure -eq [Ensure]::Present) { $result = ($null -ne $vss -and $this.Equals($vss)) } else { $this.AverageBandwidth = 100000 $this.BurstSize = 100000 $this.Enabled = $false $this.PeakBandwidth = 100000 $result = ($null -eq $vss -or $this.Equals($vss)) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostVssShaping] Get() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $result = [VMHostVssShaping]::new() $result.Server = $this.Server $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $result.Name = $vmHost.Name $this.PopulateResult($vmHost, $result) $result.Ensure = if ([string]::Empty -ne $result.VssName) { 'Present' } else { 'Absent' } return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Returns a boolean value indicating if the VMHostVssShaping should to be updated. #> [bool] Equals($vss) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vssShapingTest = @( $this.ShouldUpdateDscResourceSetting('AverageBandwidth', $vss.Spec.Policy.ShapingPolicy.AverageBandwidth, $this.AverageBandwidth), $this.ShouldUpdateDscResourceSetting('BurstSize', $vss.Spec.Policy.ShapingPolicy.BurstSize, $this.BurstSize), $this.ShouldUpdateDscResourceSetting('Enabled', $vss.Spec.Policy.ShapingPolicy.Enabled, $this.Enabled), $this.ShouldUpdateDscResourceSetting('PeakBandwidth', $vss.Spec.Policy.ShapingPolicy.PeakBandwidth, $this.PeakBandwidth) ) return ($vssShapingTest -NotContains $true) } <# .DESCRIPTION Updates the configuration of the virtual switch. #> [void] UpdateVssShaping($vmHost) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vssShapingArgs = @{ Name = $this.VssName AverageBandwidth = $this.AverageBandwidth BurstSize = $this.BurstSize Enabled = $this.Enabled PeakBandwidth = $this.PeakBandwidth } $vss = $this.GetVss() if ($this.Ensure -eq 'Present') { if ($this.Equals($vss)) { return } $vssShapingArgs.Add('Operation', 'edit') } else { $vssShapingArgs.AverageBandwidth = 100000 $vssShapingArgs.BurstSize = 100000 $vssShapingArgs.Enabled = $false $vssShapingArgs.PeakBandwidth = 100000 $vssShapingArgs.Add('Operation', 'edit') } try { Update-Network -NetworkSystem $this.vmHostNetworkSystem -VssShapingConfig $vssShapingArgs -ErrorAction Stop } catch { throw "The Virtual Switch Shaping Policy Config could not be updated: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Security settings of the Virtual Switch. #> [void] PopulateResult($vmHost, $vmHostVSSShaping) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $currentVss = $this.GetVss() if ($null -ne $currentVss) { $vmHostVSSShaping.VssName = $currentVss.Name $vmHostVSSShaping.AverageBandwidth = $currentVss.Spec.Policy.ShapingPolicy.AverageBandwidth $vmHostVSSShaping.BurstSize = $currentVss.Spec.Policy.ShapingPolicy.BurstSize $vmHostVSSShaping.Enabled = $currentVss.Spec.Policy.ShapingPolicy.Enabled $vmHostVSSShaping.PeakBandwidth = $currentVss.Spec.Policy.ShapingPolicy.PeakBandwidth } else { $vmHostVSSShaping.VssName = $this.Name $vmHostVSSShaping.AverageBandwidth = $this.AverageBandwidth $vmHostVSSShaping.BurstSize = $this.BurstSize $vmHostVSSShaping.Enabled = $this.Enabled $vmHostVSSShaping.PeakBandwidth = $this.PeakBandwidth } } } [DscResource()] class VMHostVssTeaming : VMHostVssBaseDSC { <# .DESCRIPTION The flag to indicate whether or not to enable beacon probing as a method to validate the link status of a physical network adapter. #> [DscProperty()] [nullable[bool]] $CheckBeacon <# .DESCRIPTION List of active network adapters used for load balancing. #> [DscProperty()] [string[]] $ActiveNic <# .DESCRIPTION Standby network adapters used for failover. #> [DscProperty()] [string[]] $StandbyNic <# .DESCRIPTION Flag to specify whether or not to notify the physical switch if a link fails. #> [DscProperty()] [nullable[bool]] $NotifySwitches <# .DESCRIPTION Network adapter teaming policy. #> [DscProperty()] [NicTeamingPolicy] $Policy = [NicTeamingPolicy]::Unset <# .DESCRIPTION The flag to indicate whether or not to use a rolling policy when restoring links. #> [DscProperty()] [nullable[bool]] $RollingOrder hidden [string] $PhysicalNicNotInBridgeMessage = "Physical network adapter {0} is not in the bridge with standard switch {1}." [void] Set() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $this.UpdateVssTeaming($vmHost) } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $vss = $this.GetVss() $result = $null if ($this.Ensure -eq [Ensure]::Present) { $result = ($null -ne $vss -and $this.Equals($vss)) } else { $this.CheckBeacon = $false $this.ActiveNic = @() $this.StandbyNic = @() $this.NotifySwitches = $true $this.Policy = [NicTeamingPolicy]::Loadbalance_srcid $this.RollingOrder = $false $result = ($null -eq $vss -or $this.Equals($vss)) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [VMHostVssTeaming] Get() { try { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ConnectVIServer() $result = [VMHostVssTeaming]::new() $result.Server = $this.Server $vmHost = $this.GetVMHost() $this.GetNetworkSystem($vmHost) $result.Name = $vmHost.Name $this.PopulateResult($vmHost, $result) $result.Ensure = if ([string]::Empty -ne $result.VssName) { 'Present' } else { 'Absent' } return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Returns a boolean value indicating if the VMHostVssTeaming should to be updated. #> [bool] Equals($vss) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $vssTeamingTest = @( $this.ShouldUpdateDscResourceSetting('CheckBeacon', $vss.Spec.Policy.NicTeaming.FailureCriteria.CheckBeacon, $this.CheckBeacon), $this.ShouldUpdateDscResourceSetting('NotifySwitches', $vss.Spec.Policy.NicTeaming.NotifySwitches, $this.NotifySwitches), $this.ShouldUpdateDscResourceSetting('RollingOrder', $vss.Spec.Policy.NicTeaming.RollingOrder, $this.RollingOrder), $this.ShouldUpdateDscResourceSetting('Policy', [string] $vss.Spec.Policy.NicTeaming.Policy, $this.Policy.ToString().ToLower()), $this.ShouldUpdateArraySetting('ActiveNic', $vss.Spec.Policy.NicTeaming.NicOrder.ActiveNic, $this.ActiveNic), $this.ShouldUpdateArraySetting('StandbyNic', $vss.Spec.Policy.NicTeaming.NicOrder.StandbyNic, $this.StandbyNic) ) return ($vssTeamingTest -NotContains $true) } <# .DESCRIPTION Validates that all provided physical network adapters: (ActiveNic and StandbyNic) are in the bridge with the specified standard switch. #> [void] ValidatePhysicalNetworkAdapters() { $physicalNics = $this.ActiveNic + $this.StandbyNic if ($physicalNics.Length -gt 0) { $standardSwitch = $this.GetVss() foreach ($physicalNic in $physicalNics) { if (!($standardSwitch.Spec.Bridge.NicDevice -Contains $physicalNic)) { throw ($this.PhysicalNicNotInBridgeMessage -f $physicalNic, $standardSwitch.Name) } } } } <# .DESCRIPTION Updates the configuration of the virtual switch. #> [void] UpdateVssTeaming($vmHost) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $this.ValidatePhysicalNetworkAdapters() $vssTeamingArgs = @{ Name = $this.VssName ActiveNic = $this.ActiveNic StandbyNic = $this.StandbyNic NotifySwitches = $this.NotifySwitches RollingOrder = $this.RollingOrder } if ($null -ne $this.CheckBeacon) { $vssTeamingArgs.CheckBeacon = $this.CheckBeacon } if ($this.Policy -ne [NicTeamingPolicy]::Unset) { $vssTeamingArgs.Policy = $this.Policy.ToString().ToLower() } $vss = $this.GetVss() if ($this.Ensure -eq 'Present') { if ($this.Equals($vss)) { return } $vssTeamingArgs.Add('Operation', 'edit') } else { $vssTeamingArgs.CheckBeacon = $false $vssTeamingArgs.ActiveNic = @() $vssTeamingArgs.StandbyNic = @() $vssTeamingArgs.NotifySwitches = $true $vssTeamingArgs.Policy = ([NicTeamingPolicy]::Loadbalance_srcid).ToString().ToLower() $vssTeamingArgs.RollingOrder = $false $vssTeamingArgs.Add('Operation', 'edit') } try { Update-Network -NetworkSystem $this.vmHostNetworkSystem -VssTeamingConfig $vssTeamingArgs -ErrorAction Stop } catch { throw "The Virtual Switch Teaming Policy Config could not be updated: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Security settings of the Virtual Switch. #> [void] PopulateResult($vmHost, $vmHostVSSTeaming) { $this.WriteLogUtil('Verbose', "{0} Entering {1}", @((Get-Date), (Get-PSCallStack)[0].FunctionName)) $currentVss = $this.GetVss() if ($null -ne $currentVss) { $vmHostVSSTeaming.VssName = $currentVss.Name $vmHostVSSTeaming.CheckBeacon = $currentVss.Spec.Policy.NicTeaming.FailureCriteria.CheckBeacon $vmHostVSSTeaming.ActiveNic = $currentVss.Spec.Policy.NicTeaming.NicOrder.ActiveNic $vmHostVSSTeaming.StandbyNic = $currentVss.Spec.Policy.NicTeaming.NicOrder.StandbyNic $vmHostVSSTeaming.NotifySwitches = $currentVss.Spec.Policy.NicTeaming.NotifySwitches $vmHostVSSTeaming.Policy = [NicTeamingPolicy]$currentVss.Spec.Policy.NicTeaming.Policy $vmHostVSSTeaming.RollingOrder = $currentVss.Spec.Policy.NicTeaming.RollingOrder } else { $vmHostVSSTeaming.VssName = $this.Name $vmHostVSSTeaming.CheckBeacon = $this.CheckBeacon $vmHostVSSTeaming.ActiveNic = $this.ActiveNic $vmHostVSSTeaming.StandbyNic = $this.StandbyNic $vmHostVSSTeaming.NotifySwitches = $this.NotifySwitches $vmHostVSSTeaming.Policy = $this.Policy $vmHostVSSTeaming.RollingOrder = $this.RollingOrder } } } [DscResource()] class DrsCluster : DatacenterInventoryBaseDSC { DrsCluster() { $this.InventoryItemFolderType = [FolderType]::Host } <# .DESCRIPTION Indicates that VMware DRS (Distributed Resource Scheduler) is enabled. #> [DscProperty()] [nullable[bool]] $DrsEnabled <# .DESCRIPTION Specifies a DRS (Distributed Resource Scheduler) automation level. The valid values are FullyAutomated, Manual, PartiallyAutomated, Disabled and Unset. #> [DscProperty()] [DrsAutomationLevel] $DrsAutomationLevel = [DrsAutomationLevel]::Unset <# .DESCRIPTION Threshold for generated ClusterRecommendations. DRS generates only those recommendations that are above the specified vmotionRate. Ratings vary from 1 to 5. This setting applies to Manual, PartiallyAutomated, and FullyAutomated DRS Clusters. #> [DscProperty()] [nullable[int]] $DrsMigrationThreshold <# .DESCRIPTION For availability, distributes a more even number of virtual machines across hosts. #> [DscProperty()] [nullable[int]] $DrsDistribution <# .DESCRIPTION Load balance based on consumed memory of virtual machines rather than active memory. This setting is recommended for clusters where host memory is not over-committed. #> [DscProperty()] [nullable[int]] $MemoryLoadBalancing <# .DESCRIPTION Controls CPU over-commitment in the cluster. Min value is 0 and Max value is 500. #> [DscProperty()] [nullable[int]] $CPUOverCommitment hidden [string] $DrsEnabledConfigPropertyName = 'Enabled' hidden [string] $DrsAutomationLevelConfigPropertyName = 'DefaultVmBehavior' hidden [string] $DrsMigrationThresholdConfigPropertyName = 'VmotionRate' hidden [string] $DrsDistributionSettingName = 'LimitVMsPerESXHostPercent' hidden [string] $MemoryLoadBalancingSettingName = 'PercentIdleMBInMemDemand' hidden [string] $CPUOverCommitmentSettingName = 'MaxVcpusPerClusterPct' [void] Set() { try { $this.ConnectVIServer() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $clusterLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $cluster = $this.GetInventoryItem($clusterLocation) if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $cluster) { $this.AddCluster($clusterLocation) } else { $this.UpdateCluster($cluster) } } else { if ($null -ne $cluster) { $this.RemoveCluster($cluster) } } } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $clusterLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $cluster = $this.GetInventoryItem($clusterLocation) $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $cluster) { $result = $false } else { $result = !$this.ShouldUpdateCluster($cluster) } } else { $result = ($null -eq $cluster) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [DrsCluster] Get() { try { $result = [DrsCluster]::new() $result.Server = $this.Server $result.Location = $this.Location $result.DatacenterName = $this.DatacenterName $result.DatacenterLocation = $this.DatacenterLocation $this.ConnectVIServer() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $clusterLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $cluster = $this.GetInventoryItem($clusterLocation) $this.PopulateResult($cluster, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Checks if the Cluster should be updated. #> [bool] ShouldUpdateCluster($cluster) { $drsConfig = $cluster.ExtensionData.ConfigurationEx.DrsConfig $currentDrsDistributionOption = ($drsConfig.Option | Where-Object -FilterScript { $_.Key -eq $this.DrsDistributionSettingName }).Value $currentMemoryLoadBalancingOption = ($drsConfig.Option | Where-Object -FilterScript { $_.Key -eq $this.MemoryLoadBalancingSettingName }).Value $currentCPUOverCommitmentOption = ($drsConfig.Option | Where-Object -FilterScript { $_.Key -eq $this.CPUOverCommitmentSettingName }).Value $shouldUpdateCluster = @( $this.ShouldUpdateDscResourceSetting('DrsEnabled', $drsConfig.Enabled, $this.DrsEnabled), $this.ShouldUpdateDscResourceSetting('DrsAutomationLevel', [string] $drsConfig.DefaultVmBehavior, $this.DrsAutomationLevel.ToString()), $this.ShouldUpdateDscResourceSetting('DrsMigrationThreshold', $drsConfig.VmotionRate, $this.DrsMigrationThreshold), $this.ShouldUpdateDscResourceSetting('DrsDistribution', $currentDrsDistributionOption, $this.DrsDistribution), $this.ShouldUpdateDscResourceSetting('MemoryLoadBalancing', $currentMemoryLoadBalancingOption, $this.MemoryLoadBalancing), $this.ShouldUpdateDscResourceSetting('CPUOverCommitment', $currentCPUOverCommitmentOption, $this.CPUOverCommitment) ) return ($shouldUpdateCluster -Contains $true) } <# .DESCRIPTION Populates the DrsConfig property with the desired value. #> [void] PopulateDrsConfigProperty($drsConfig, $propertyName, $propertyValue) { <# Special case where the passed property value is enum type. These type of properties should be populated only when their value is not equal to Unset. Unset means that the property was not specified in the Configuration. #> if ($propertyValue -is [DrsAutomationLevel]) { if ($propertyValue -ne [DrsAutomationLevel]::Unset) { $drsConfig.$propertyName = $propertyValue.ToString() } } elseif ($null -ne $propertyValue) { $drsConfig.$propertyName = $propertyValue } } <# .DESCRIPTION Returns the Option array for the DrsConfig with the specified options in the Configuration. #> [PSObject] GetOptionsForDrsConfig($allOptions) { $drsConfigOptions = @() foreach ($key in $allOptions.Keys) { if ($null -ne $allOptions.$key) { $option = New-Object VMware.Vim.OptionValue $option.Key = $key $option.Value = $allOptions.$key.ToString() $drsConfigOptions += $option } } return $drsConfigOptions } <# .DESCRIPTION Returns the populated Cluster Spec with the specified values in the Configuration. #> [PSObject] GetPopulatedClusterSpec() { $clusterSpec = New-Object VMware.Vim.ClusterConfigSpecEx $clusterSpec.DrsConfig = New-Object VMware.Vim.ClusterDrsConfigInfo $this.PopulateDrsConfigProperty($clusterSpec.DrsConfig, $this.DrsEnabledConfigPropertyName, $this.DrsEnabled) $this.PopulateDrsConfigProperty($clusterSpec.DrsConfig, $this.DrsAutomationLevelConfigPropertyName, $this.DrsAutomationLevel) $this.PopulateDrsConfigProperty($clusterSpec.DrsConfig, $this.DrsMigrationThresholdConfigPropertyName, $this.DrsMigrationThreshold) $allOptions = [ordered] @{ $this.DrsDistributionSettingName = $this.DrsDistribution $this.MemoryLoadBalancingSettingName = $this.MemoryLoadBalancing $this.CPUOverCommitmentSettingName = $this.CPUOverCommitment } $clusterSpec.DrsConfig.Option = $this.GetOptionsForDrsConfig($allOptions) return $clusterSpec } <# .DESCRIPTION Creates a new Cluster with the specified properties at the specified location. #> [void] AddCluster($clusterLocation) { $clusterSpec = $this.GetPopulatedClusterSpec() try { Add-Cluster -Folder $clusterLocation.ExtensionData -Name $this.Name -Spec $clusterSpec } catch { throw "Server operation failed with the following error: $($_.Exception.Message)" } } <# .DESCRIPTION Updates the Cluster with the specified properties. #> [void] UpdateCluster($cluster) { $clusterSpec = $this.GetPopulatedClusterSpec() try { Update-ClusterComputeResource -ClusterComputeResource $cluster.ExtensionData -Spec $clusterSpec } catch { throw "Server operation failed with the following error: $($_.Exception.Message)" } } <# .DESCRIPTION Removes the Cluster from the specified Datacenter. #> [void] RemoveCluster($cluster) { try { Remove-ClusterComputeResource -ClusterComputeResource $cluster.ExtensionData } catch { throw "Server operation failed with the following error: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Cluster from the server. #> [void] PopulateResult($cluster, $result) { if ($null -ne $cluster) { $drsConfig = $cluster.ExtensionData.ConfigurationEx.DrsConfig $result.Name = $cluster.Name $result.Ensure = [Ensure]::Present $result.DrsEnabled = $drsConfig.Enabled if ($null -eq $drsConfig.DefaultVmBehavior) { $result.DrsAutomationLevel = [DrsAutomationLevel]::Unset } else { $result.DrsAutomationLevel = $drsConfig.DefaultVmBehavior.ToString() } $result.DrsMigrationThreshold = $drsConfig.VmotionRate if ($null -ne $drsConfig.Option) { $options = $drsConfig.Option $result.DrsDistribution = ($options | Where-Object { $_.Key -eq $this.DrsDistributionSettingName }).Value $result.MemoryLoadBalancing = ($options | Where-Object { $_.Key -eq $this.MemoryLoadBalancingSettingName }).Value $result.CPUOverCommitment = ($options | Where-Object { $_.Key -eq $this.CPUOverCommitmentSettingName }).Value } else { $result.DrsDistribution = $null $result.MemoryLoadBalancing = $null $result.CPUOverCommitment = $null } } else { $result.Name = $this.Name $result.Ensure = [Ensure]::Absent $result.DrsEnabled = $this.DrsEnabled $result.DrsAutomationLevel = $this.DrsAutomationLevel $result.DrsMigrationThreshold = $this.DrsMigrationThreshold $result.DrsDistribution = $this.DrsDistribution $result.MemoryLoadBalancing = $this.MemoryLoadBalancing $result.CPUOverCommitment = $this.CPUOverCommitment } } } [DscResource()] class HACluster : DatacenterInventoryBaseDSC { HACluster() { $this.InventoryItemFolderType = [FolderType]::Host } <# .DESCRIPTION Indicates that VMware HA (High Availability) is enabled. #> [DscProperty()] [nullable[bool]] $HAEnabled <# .DESCRIPTION Indicates that virtual machines cannot be powered on if they violate availability constraints. #> [DscProperty()] [nullable[bool]] $HAAdmissionControlEnabled <# .DESCRIPTION Specifies a configured failover level. This is the number of physical host failures that can be tolerated without impacting the ability to meet minimum thresholds for all running virtual machines. The valid values range from 1 to 4. #> [DscProperty()] [nullable[int]] $HAFailoverLevel <# .DESCRIPTION Indicates that the virtual machine should be powered off if a host determines that it is isolated from the rest of the compute resource. The valid values are PowerOff, DoNothing, Shutdown and Unset. #> [DscProperty()] [HAIsolationResponse] $HAIsolationResponse = [HAIsolationResponse]::Unset <# .DESCRIPTION Specifies the cluster HA restart priority. The valid values are Disabled, Low, Medium, High and Unset. VMware HA is a feature that detects failed virtual machines and automatically restarts them on alternative ESX hosts. #> [DscProperty()] [HARestartPriority] $HARestartPriority = [HARestartPriority]::Unset hidden [string] $HAEnabledParameterName = 'HAEnabled' hidden [string] $HAAdmissionControlEnabledParameterName = 'HAAdmissionControlEnabled' hidden [string] $HAFailoverLevelParameterName = 'HAFailoverLevel' hidden [string] $HAIsolationResponseParameterName = 'HAIsolationResponse' hidden [string] $HARestartPriorityParemeterName = 'HARestartPriority' [void] Set() { try { $this.ConnectVIServer() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $clusterLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $cluster = $this.GetInventoryItem($clusterLocation) if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $cluster) { $this.AddCluster($clusterLocation) } else { $this.UpdateCluster($cluster) } } else { if ($null -ne $cluster) { $this.RemoveCluster($cluster) } } } finally { $this.DisconnectVIServer() } } [bool] Test() { try { $this.ConnectVIServer() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $clusterLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $cluster = $this.GetInventoryItem($clusterLocation) $result = $null if ($this.Ensure -eq [Ensure]::Present) { if ($null -eq $cluster) { $result = $false } else { $result = !$this.ShouldUpdateCluster($cluster) } } else { $result = ($null -eq $cluster) } $this.WriteDscResourceState($result) return $result } finally { $this.DisconnectVIServer() } } [HACluster] Get() { try { $result = [HACluster]::new() $result.Server = $this.Server $result.Location = $this.Location $result.DatacenterName = $this.DatacenterName $result.DatacenterLocation = $this.DatacenterLocation $this.ConnectVIServer() $datacenter = $this.GetDatacenter() $datacenterFolderName = "$($this.InventoryItemFolderType)Folder" $clusterLocation = $this.GetInventoryItemLocationInDatacenter($datacenter, $datacenterFolderName) $cluster = $this.GetInventoryItem($clusterLocation) $this.PopulateResult($cluster, $result) return $result } finally { $this.DisconnectVIServer() } } <# .DESCRIPTION Checks if the Cluster should be updated. #> [bool] ShouldUpdateCluster($cluster) { $shouldUpdateCluster = @( $this.ShouldUpdateDscResourceSetting('HAEnabled', $cluster.HAEnabled, $this.HAEnabled), $this.ShouldUpdateDscResourceSetting('HAAdmissionControlEnabled', $cluster.HAAdmissionControlEnabled, $this.HAAdmissionControlEnabled), $this.ShouldUpdateDscResourceSetting('HAFailoverLevel', $cluster.HAFailoverLevel, $this.HAFailoverLevel), $this.ShouldUpdateDscResourceSetting('HAIsolationResponse', [string] $cluster.HAIsolationResponse, $this.HAIsolationResponse.ToString()), $this.ShouldUpdateDscResourceSetting('HARestartPriority', [string] $cluster.HARestartPriority, $this.HARestartPriority.ToString()) ) return ($shouldUpdateCluster -Contains $true) } <# .DESCRIPTION Populates the parameters for the New-Cluster and Set-Cluster cmdlets. #> [void] PopulateClusterParams($clusterParams, $parameter, $desiredValue) { <# Special case where the desired value is enum type. These type of properties should be added as parameters to the cmdlet only when their value is not equal to Unset. Unset means that the property was not specified in the Configuration. #> if ($desiredValue -is [HAIsolationResponse] -or $desiredValue -is [HARestartPriority]) { if ($desiredValue -ne 'Unset') { $clusterParams.$parameter = $desiredValue.ToString() } return } if ($null -ne $desiredValue) { $clusterParams.$parameter = $desiredValue } } <# .DESCRIPTION Returns the populated Cluster parameters. #> [hashtable] GetClusterParams() { $clusterParams = @{} $clusterParams.Server = $this.Connection $clusterParams.Confirm = $false $clusterParams.ErrorAction = 'Stop' $this.PopulateClusterParams($clusterParams, $this.HAEnabledParameterName, $this.HAEnabled) # High Availability settings cannot be passed to the cmdlets if 'HAEnabled' is $false. if ($null -eq $this.HAEnabled -or $this.HAEnabled) { $this.PopulateClusterParams($clusterParams, $this.HAAdmissionControlEnabledParameterName, $this.HAAdmissionControlEnabled) $this.PopulateClusterParams($clusterParams, $this.HAFailoverLevelParameterName, $this.HAFailoverLevel) $this.PopulateClusterParams($clusterParams, $this.HAIsolationResponseParameterName, $this.HAIsolationResponse) $this.PopulateClusterParams($clusterParams, $this.HARestartPriorityParemeterName, $this.HARestartPriority) } return $clusterParams } <# .DESCRIPTION Creates a new Cluster with the specified properties at the specified location. #> [void] AddCluster($clusterLocation) { $clusterParams = $this.GetClusterParams() $clusterParams.Name = $this.Name $clusterParams.Location = $clusterLocation try { New-Cluster @clusterParams } catch { throw "Cannot create Cluster $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Updates the Cluster with the specified properties. #> [void] UpdateCluster($cluster) { $clusterParams = $this.GetClusterParams() try { $cluster | Set-Cluster @clusterParams } catch { throw "Cannot update Cluster $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Removes the Cluster from the specified Datacenter. #> [void] RemoveCluster($cluster) { try { $cluster | Remove-Cluster -Server $this.Connection -Confirm:$false -ErrorAction Stop } catch { throw "Cannot remove Cluster $($this.Name). For more information: $($_.Exception.Message)" } } <# .DESCRIPTION Populates the result returned from the Get() method with the values of the Cluster from the server. #> [void] PopulateResult($cluster, $result) { if ($null -ne $cluster) { $result.Name = $cluster.Name $result.Ensure = [Ensure]::Present $result.HAEnabled = $cluster.HAEnabled $result.HAAdmissionControlEnabled = $cluster.HAAdmissionControlEnabled $result.HAFailoverLevel = $cluster.HAFailoverLevel $result.HAIsolationResponse = $cluster.HAIsolationResponse.ToString() $result.HARestartPriority = $cluster.HARestartPriority.ToString() } else { $result.Name = $this.Name $result.Ensure = [Ensure]::Absent $result.HAEnabled = $this.HAEnabled $result.HAAdmissionControlEnabled = $this.HAAdmissionControlEnabled $result.HAFailoverLevel = $this.HAFailoverLevel $result.HAIsolationResponse = $this.HAIsolationResponse $result.HARestartPriority = $this.HARestartPriority } } } # SIG # Begin signature block # MIIi9AYJKoZIhvcNAQcCoIIi5TCCIuECAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCvl2+8BOuROqnZ # CL4ez9wOZH5dQr7rxxvEjHnAMDLrB6CCD8swggTMMIIDtKADAgECAhBdqtQcwalQ # C13tonk09GI7MA0GCSqGSIb3DQEBCwUAMH8xCzAJBgNVBAYTAlVTMR0wGwYDVQQK # ExRTeW1hbnRlYyBDb3Jwb3JhdGlvbjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3Qg # TmV0d29yazEwMC4GA1UEAxMnU3ltYW50ZWMgQ2xhc3MgMyBTSEEyNTYgQ29kZSBT # aWduaW5nIENBMB4XDTE4MDgxMzAwMDAwMFoXDTIxMDkxMTIzNTk1OVowZDELMAkG # A1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCVBhbG8gQWx0 # bzEVMBMGA1UECgwMVk13YXJlLCBJbmMuMRUwEwYDVQQDDAxWTXdhcmUsIEluYy4w # ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCuswYfqnKot0mNu9VhCCCR # vVcCrxoSdB6G30MlukAVxgQ8qTyJwr7IVBJXEKJYpzv63/iDYiNAY3MOW+Pb4qGI # bNpafqxc2WLW17vtQO3QZwscIVRapLV1xFpwuxJ4LYdsxHPZaGq9rOPBOKqTP7Jy # KQxE/1ysjzacA4NXHORf2iars70VpZRksBzkniDmurvwCkjtof+5krxXd9XSDEFZ # 9oxeUGUOBCvSLwOOuBkWPlvCnzEqMUeSoXJavl1QSJvUOOQeoKUHRycc54S6Lern # 2ddmdUDPwjD2cQ3PL8cgVqTsjRGDrCgOT7GwShW3EsRsOwc7o5nsiqg/x7ZmFpSJ # AgMBAAGjggFdMIIBWTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIHgDArBgNVHR8E # JDAiMCCgHqAchhpodHRwOi8vc3Yuc3ltY2IuY29tL3N2LmNybDBhBgNVHSAEWjBY # MFYGBmeBDAEEATBMMCMGCCsGAQUFBwIBFhdodHRwczovL2Quc3ltY2IuY29tL2Nw # czAlBggrBgEFBQcCAjAZDBdodHRwczovL2Quc3ltY2IuY29tL3JwYTATBgNVHSUE # DDAKBggrBgEFBQcDAzBXBggrBgEFBQcBAQRLMEkwHwYIKwYBBQUHMAGGE2h0dHA6 # Ly9zdi5zeW1jZC5jb20wJgYIKwYBBQUHMAKGGmh0dHA6Ly9zdi5zeW1jYi5jb20v # c3YuY3J0MB8GA1UdIwQYMBaAFJY7U/B5M5evfYPvLivMyreGHnJmMB0GA1UdDgQW # BBTVp9RQKpAUKYYLZ70Ta983qBUJ1TANBgkqhkiG9w0BAQsFAAOCAQEAlnsx3io+ # W/9i0QtDDhosvG+zTubTNCPtyYpv59Nhi81M0GbGOPNO3kVavCpBA11Enf0CZuEq # f/ctbzYlMRONwQtGZ0GexfD/RhaORSKib/ACt70siKYBHyTL1jmHfIfi2yajKkMx # UrPM9nHjKeagXTCGthD/kYW6o7YKKcD7kQUyBhofimeSgumQlm12KSmkW0cHwSSX # TUNWtshVz+74EcnZtGFI6bwYmhvnTp05hWJ8EU2Y1LdBwgTaRTxlSDP9JK+e63vm # SXElMqnn1DDXABT5RW8lNt6g9P09a2J8p63JGgwMBhmnatw7yrMm5EAo+K6gVliJ # LUMlTW3O09MbDTCCBVkwggRBoAMCAQICED141/l2SWCyYX308B7KhiowDQYJKoZI # hvcNAQELBQAwgcoxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5j # LjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMp # IDIwMDYgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFF # MEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZp # Y2F0aW9uIEF1dGhvcml0eSAtIEc1MB4XDTEzMTIxMDAwMDAwMFoXDTIzMTIwOTIz # NTk1OVowfzELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0 # aW9uMR8wHQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMTAwLgYDVQQDEydT # eW1hbnRlYyBDbGFzcyAzIFNIQTI1NiBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqG # SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCXgx4AFq8ssdIIxNdok1FgHnH24ke021hN # I2JqtL9aG1H3ow0Yd2i72DarLyFQ2p7z518nTgvCl8gJcJOp2lwNTqQNkaC07BTO # kXJULs6j20TpUhs/QTzKSuSqwOg5q1PMIdDMz3+b5sLMWGqCFe49Ns8cxZcHJI7x # e74xLT1u3LWZQp9LYZVfHHDuF33bi+VhiXjHaBuvEXgamK7EVUdT2bMy1qEORkDF # l5KK0VOnmVuFNVfT6pNiYSAKxzB3JBFNYoO2untogjHuZcrf+dWNsjXcjCtvanJc # YISc8gyUXsBWUgBIzNP4pX3eL9cT5DiohNVGuBOGwhud6lo43ZvbAgMBAAGjggGD # MIIBfzAvBggrBgEFBQcBAQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9zMi5zeW1j # Yi5jb20wEgYDVR0TAQH/BAgwBgEB/wIBADBsBgNVHSAEZTBjMGEGC2CGSAGG+EUB # BxcDMFIwJgYIKwYBBQUHAgEWGmh0dHA6Ly93d3cuc3ltYXV0aC5jb20vY3BzMCgG # CCsGAQUFBwICMBwaGmh0dHA6Ly93d3cuc3ltYXV0aC5jb20vcnBhMDAGA1UdHwQp # MCcwJaAjoCGGH2h0dHA6Ly9zMS5zeW1jYi5jb20vcGNhMy1nNS5jcmwwHQYDVR0l # BBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIBBjApBgNVHREE # IjAgpB4wHDEaMBgGA1UEAxMRU3ltYW50ZWNQS0ktMS01NjcwHQYDVR0OBBYEFJY7 # U/B5M5evfYPvLivMyreGHnJmMB8GA1UdIwQYMBaAFH/TZafC3ey78DAJ80M5+gKv # MzEzMA0GCSqGSIb3DQEBCwUAA4IBAQAThRoeaak396C9pK9+HWFT/p2MXgymdR54 # FyPd/ewaA1U5+3GVx2Vap44w0kRaYdtwb9ohBcIuc7pJ8dGT/l3JzV4D4ImeP3Qe # 1/c4i6nWz7s1LzNYqJJW0chNO4LmeYQW/CiwsUfzHaI+7ofZpn+kVqU/rYQuKd58 # vKiqoz0EAeq6k6IOUCIpF0yH5DoRX9akJYmbBWsvtMkBTCd7C6wZBSKgYBU/2sn7 # TUyP+3Jnd/0nlMe6NQ6ISf6N/SivShK9DbOXBd5EDBX6NisD3MFQAfGhEV0U5eK9 # J0tUviuEXg+mw3QFCu+Xw4kisR93873NQ9TxTKk/tYuEr2Ty0BQhMIIFmjCCA4Kg # AwIBAgIKYRmT5AAAAAAAHDANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQDEyBNaWNyb3NvZnQgQ29kZSBW # ZXJpZmljYXRpb24gUm9vdDAeFw0xMTAyMjIxOTI1MTdaFw0yMTAyMjIxOTM1MTda # MIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV # BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA2IFZl # cmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMT # PFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB # dXRob3JpdHkgLSBHNTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8k # CAgpejWeYAyq50s7Ttx8vDxFHLsr4P4pAvlXCKNkhRUn9fGtyDGJXSLoKqqmQrOP # +LlVt7G3S7P+j34HV+zvQ9tmYhVhz2ANpNje+ODDYgg9VBPrScpZVIUm5SuPG5/r # 9aGRwjNJ2ENjalJL0o/ocFFN0Ylpe8dw9rPcEnTbe11LVtOWvxV3obD0oiXyrxyS # Zxjl9AYE75C55ADk3Tq1Gf8CuvQ87uCL6zeL7PTXrPL28D2v3XWRMxkdHEDLdCQZ # IZPZFP6sKlLHj9UESeSNY0eIPGmDy/5HvSt+T8WVrg6d1NFDwGdz4xQIfuU/n3O4 # MwrPXT80h5aK7lPoJRUCAwEAAaOByzCByDARBgNVHSAECjAIMAYGBFUdIAAwDwYD # VR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAYYwHQYDVR0OBBYEFH/TZafC3ey78DAJ # 80M5+gKvMzEzMB8GA1UdIwQYMBaAFGL7CiFbf0NuEdoJVFBr9dKWcfGeMFUGA1Ud # HwROMEwwSqBIoEaGRGh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3By # b2R1Y3RzL01pY3Jvc29mdENvZGVWZXJpZlJvb3QuY3JsMA0GCSqGSIb3DQEBBQUA # A4ICAQCBKoIWjDRnK+UD6zR7jKKjUIr0VYbxHoyOrn3uAxnOcpUYSK1iEf0g/T9H # BgFa4uBvjBUsTjxqUGwLNqPPeg2cQrxc+BnVYONp5uIjQWeMaIN2K4+Toyq1f75Z # +6nJsiaPyqLzghuYPpGVJ5eGYe5bXQdrzYao4mWAqOIV4rK+IwVqugzzR5NNrKSM # B3k5wGESOgUNiaPsn1eJhPvsynxHZhSR2LYPGV3muEqsvEfIcUOW5jIgpdx3hv08 # 44tx23ubA/y3HTJk6xZSoEOj+i6tWZJOfMfyM0JIOFE6fDjHGyQiKEAeGkYfF9sY # 9/AnNWy4Y9nNuWRdK6Ve78YptPLH+CHMBLpX/QG2q8Zn+efTmX/09SL6cvX9/zoc # Qjqh+YAYpe6NHNRmnkUB/qru//sXjzD38c0pxZ3stdVJAD2FuMu7kzonaknAMK5m # yfcjKDJ2+aSDVshIzlqWqqDMDMR/tI6Xr23jVCfDn4bA1uRzCJcF29BUYl4DSMLV # n3+nZozQnbBP1NOYX0t6yX+yKVLQEoDHD1S2HmfNxqBsEQOE00h15yr+sDtuCjqm # a3aZBaPxd2hhMxRHBvxTf1K9khRcSiRqZ4yvjZCq0PZ5IRuTJnzDzh69iDiSrkXG # GWpJULMF+K5ZN4pqJQOUsVmBUOi6g4C3IzX0drlnHVkYrSCNlDGCEn8wghJ7AgEB # MIGTMH8xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3JhdGlv # bjEfMB0GA1UECxMWU3ltYW50ZWMgVHJ1c3QgTmV0d29yazEwMC4GA1UEAxMnU3lt # YW50ZWMgQ2xhc3MgMyBTSEEyNTYgQ29kZSBTaWduaW5nIENBAhBdqtQcwalQC13t # onk09GI7MA0GCWCGSAFlAwQCAQUAoIGWMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3 # AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCoGCisGAQQBgjcCAQwx # HDAaoRiAFmh0dHA6Ly93d3cudm13YXJlLmNvbS8wLwYJKoZIhvcNAQkEMSIEINQL # gD8Emff7jf0+qJ5BCrKfbqZj5SpHw8AV9knohkf/MA0GCSqGSIb3DQEBAQUABIIB # AEL4JQzLHvmG2YKPMbFzLQK19sKiaEUnXmBqIon0j4MTVVMcN4LzOt5B8Y8PQLZ9 # 61FWmHXsqSRvbva6BAbB81rpHw4VCd18UubzFODBTyob8yfrsGXHlL3gVfqJTnZ5 # /GWU0WuNPPacM1AKOpBPeM9swcrANb+mmUC/47Ci6Zsowln/Kax/wXqA0k7DyJpi # MQh9HHqc8+LsybCG7uaRa4MLZDPh1I2eL2vkJXYr53q1Qx3fUEBonM5LAsoj5PJg # RjYjiEde7e4stTCAXJxWRcgOrHIWW0zLLjw7GHraE0EP0eAEoH/HBEOXlcXbSyht # yB2bfnhvGGSaOrI5yV2l64uhghAjMIIQHwYKKwYBBAGCNwMDATGCEA8wghALBgkq # hkiG9w0BBwKggg/8MIIP+AIBAzEPMA0GCWCGSAFlAwQCAQUAMIHmBgsqhkiG9w0B # CRABBKCB1gSB0zCB0AIBAQYJKwYBBAGgMgIDMDEwDQYJYIZIAWUDBAIBBQAEIAi7 # gviBSmhrKtLd8ui5ONuj/dgqc/diYvLSP9O08xFBAg4BbKiJKXgAAAAAA2OsbxgT # MjAyMTAyMjQwOTAxMTMuOTc3WjADAgEBoGOkYTBfMQswCQYDVQQGEwJKUDEcMBoG # A1UEChMTR01PIEdsb2JhbFNpZ24gSy5LLjEyMDAGA1UEAxMpR2xvYmFsU2lnbiBU # U0EgZm9yIEFkdmFuY2VkIC0gRzMgLSAwMDMtMDGgggxqMIIE6jCCA9KgAwIBAgIM # M5Agd2HEJt2UUAMNMA0GCSqGSIb3DQEBCwUAMFsxCzAJBgNVBAYTAkJFMRkwFwYD # VQQKExBHbG9iYWxTaWduIG52LXNhMTEwLwYDVQQDEyhHbG9iYWxTaWduIFRpbWVz # dGFtcGluZyBDQSAtIFNIQTI1NiAtIEcyMB4XDTE4MDYxNDEwMDAwMFoXDTI5MDMx # ODEwMDAwMFowXzELMAkGA1UEBhMCSlAxHDAaBgNVBAoTE0dNTyBHbG9iYWxTaWdu # IEsuSy4xMjAwBgNVBAMTKUdsb2JhbFNpZ24gVFNBIGZvciBBZHZhbmNlZCAtIEcz # IC0gMDAzLTAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv3Gj+IDO # E5Be8KfdP9KY8kE6Sdp/WC+ePDoBE8ptNJlbDCccROdW4wkv9W+rTr4nYmbGuLKH # x2W+xsBeqT6u+yR0iyv4aARkhqo64qohj/rxnbkYMF6afAf1O3Uu2gklGav+c+lx # neyq9j4ShYEUJPjmPpnfrvO5i9UmywSommFW7yhwqEtqKyVq5aA2ny25mofcdA4f # QqBBOpYHDst7MtUBC1ORfVY0T7S8sHRHnKp6bF/kjlGfk5BhAz6PX0FBUHg5LRIS # 3OvqADCyP+FtE7d1SBVrTg7Rl+NO25bZ0WKvCEHPIg/o3c7Y6pNWbtM6j2dKaki6 # /GHlbFmzEi0CgQIDAQABo4IBqDCCAaQwDgYDVR0PAQH/BAQDAgeAMEwGA1UdIARF # MEMwQQYJKwYBBAGgMgEeMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2Jh # bHNpZ24uY29tL3JlcG9zaXRvcnkvMAkGA1UdEwQCMAAwFgYDVR0lAQH/BAwwCgYI # KwYBBQUHAwgwRgYDVR0fBD8wPTA7oDmgN4Y1aHR0cDovL2NybC5nbG9iYWxzaWdu # LmNvbS9ncy9nc3RpbWVzdGFtcGluZ3NoYTJnMi5jcmwwgZgGCCsGAQUFBwEBBIGL # MIGIMEgGCCsGAQUFBzAChjxodHRwOi8vc2VjdXJlLmdsb2JhbHNpZ24uY29tL2Nh # Y2VydC9nc3RpbWVzdGFtcGluZ3NoYTJnMi5jcnQwPAYIKwYBBQUHMAGGMGh0dHA6 # Ly9vY3NwMi5nbG9iYWxzaWduLmNvbS9nc3RpbWVzdGFtcGluZ3NoYTJnMjAdBgNV # HQ4EFgQUeaezg3HWs0B2IOZ0Crf39+bd3XQwHwYDVR0jBBgwFoAUkiGnSpVdZLCb # tB7mADdH5p1BK0wwDQYJKoZIhvcNAQELBQADggEBAIc0fm43ZxsIEQJttimYchTL # SH7IyY8viQ2vD/IsIZBuO7ccAaqBaMQQI0v4CeOrX+pFps4O/qSA6WtqDAD5yoYQ # DD7/HxrpHOUil2TZrOnj6NpTYGMLt45P3NUh9J3eE2o4NeVs4yZM29Z0Z0W5TwTE # WAgam2ZFPSQaGpJXyV8oR3hn21zKrQvotw/RthYyNCIENnJM73umvLauBMDZeKCI # yIZrGNqWjStuIlzLf70XvZ63toZNgxBNsDKy4BOgy2DihHUU6SG9EKKktgjPOw0p # WVmp08NMDX9CzIgUtELlugTVmEqkjQc9SR94bWVtYL38zlnrLOnFqtqt7taTrBUw # ggQVMIIC/aADAgECAgsEAAAAAAExicZQBDANBgkqhkiG9w0BAQsFADBMMSAwHgYD # VQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2ln # bjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0xMTA4MDIxMDAwMDBaFw0yOTAzMjkx # MDAwMDBaMFsxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNh # MTEwLwYDVQQDEyhHbG9iYWxTaWduIFRpbWVzdGFtcGluZyBDQSAtIFNIQTI1NiAt # IEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqpuOw6sRUSUBtpaU # 4k/YwQj2RiPZRcWVl1urGr/SbFfJMwYfoA/GPH5TSHq/nYeer+7DjEfhQuzj46FK # bAwXxKbBuc1b8R5EiY7+C94hWBPuTcjFZwscsrPxNHaRossHbTfFoEcmAhWkkJGp # eZ7X61edK3wi2BTX8QceeCI2a3d5r6/5f45O4bUIMf3q7UtxYowj8QM5j0R5tnYD # V56tLwhG3NKMvPSOdM7IaGlRdhGLD10kWxlUPSbMQI2CJxtZIH1Z9pOAjvgqOP1r # oEBlH1d2zFuOBE8sqNuEUBNPxtyLufjdaUyI65x7MCb8eli7WbwUcpKBV7d2ydiA # CoBuCQIDAQABo4HoMIHlMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/ # AgEAMB0GA1UdDgQWBBSSIadKlV1ksJu0HuYAN0fmnUErTDBHBgNVHSAEQDA+MDwG # BFUdIAAwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xvYmFsc2lnbi5jb20v # cmVwb3NpdG9yeS8wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9iYWxz # aWduLm5ldC9yb290LXIzLmNybDAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpjmove # 4t0bvDANBgkqhkiG9w0BAQsFAAOCAQEABFaCSnzQzsm/NmbRvjWek2yX6AbOMRhZ # +WxBX4AuwEIluBjH/NSxN8RooM8oagN0S2OXhXdhO9cv4/W9M6KSfREfnops7yyw # 9GKNNnPRFjbxvF7stICYePzSdnno4SGU4B/EouGqZ9uznHPlQCLPOc7b5neVp7uy # y/YZhp2fyNSYBbJxb051rvE9ZGo7Xk5GpipdCJLxo/MddL9iDSOMXCo4ldLA1c3P # iNofKLW6gWlkKrWmotVzr9xG2wSukdduxZi61EfEVnSAR3hYjL7vK/3sbL/RlPe/ # UOB74JD9IBh4GCJdCC6MHKCX8x2ZfaOdkdMGRE4EbnocIOM28LZQuTCCA18wggJH # oAMCAQICCwQAAAAAASFYUwiiMA0GCSqGSIb3DQEBCwUAMEwxIDAeBgNVBAsTF0ds # b2JhbFNpZ24gUm9vdCBDQSAtIFIzMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYD # VQQDEwpHbG9iYWxTaWduMB4XDTA5MDMxODEwMDAwMFoXDTI5MDMxODEwMDAwMFow # TDEgMB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkds # b2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wggEiMA0GCSqGSIb3DQEBAQUA # A4IBDwAwggEKAoIBAQDMJXaQeQZ4Ihb1wIO2hMoonv0FdhHFrYhy/EYCQ8eyip0E # XyTLLkvhYIJG4VKrDIFHcGzdZNHr9SyjD4I9DCuul9e2FIYQebs7E4B3jAjhSdJq # Yi8fXvqWaN+JJ5U4nwbXPsnLJlkNc96wyOkmDoMVxu9bi9IEYMpJpij2aTv2y8go # keWdimFXN6x0FNx04Druci8unPvQu7/1PQDhBjPogiuuU6Y6FnOM3UEOIDrAtKeh # 6bJPkC4yYOlXy7kEkmho5TgmYHWyn3f/kRTvriBJ/K1AFUjRAjFhGV64l++td7dk # mnq/X8ET75ti+w1s4FRpFqkD2m7pg5NxdsZphYIXAgMBAAGjQjBAMA4GA1UdDwEB # /wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSP8Et/qC5FJK5NUPpj # move4t0bvDANBgkqhkiG9w0BAQsFAAOCAQEAS0DbwFCq/sgM7/eWVEVJu5YACUGs # sxOGhigHM8pr5nS5ugAtrqQK0/Xx8Q+Kv3NnSoPHRHt44K9ubG8DKY4zOUXDjuS5 # V2yq/BKW7FPGLeQkbLmUY/vcU2hnVj6DuM81IcPJaP7O2sJTqsyQiunwXUaMld16 # WCgaLx3ezQA3QY/tRG3XUyiXfvNnBB4V14qWtNPeTCekTBtzc3b0F5nCH3oO4y0I # rQocLP88q1UOD5F+NuvDV0m+4S4tfGCLw0FREyOdzvcya5QBqJnnLDMfOjsl0oZA # zjsshnjJYS8Uuu7bVW/fhO4FCU29KNhyztNiUGUe65KXgzHZs7XKR1g/XzGCAokw # ggKFAgEBMGswWzELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt # c2ExMTAvBgNVBAMTKEdsb2JhbFNpZ24gVGltZXN0YW1waW5nIENBIC0gU0hBMjU2 # IC0gRzICDDOQIHdhxCbdlFADDTANBglghkgBZQMEAgEFAKCB8DAaBgkqhkiG9w0B # CQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEILCTy9g/O4AcqVvqVY+j # QTD1SmpHgrdJapkfXmz+A35YMIGgBgsqhkiG9w0BCRACDDGBkDCBjTCBijCBhwQU # rmsC2QsljAmRsRYSid62aVY5HW8wbzBfpF0wWzELMAkGA1UEBhMCQkUxGTAXBgNV # BAoTEEdsb2JhbFNpZ24gbnYtc2ExMTAvBgNVBAMTKEdsb2JhbFNpZ24gVGltZXN0 # YW1waW5nIENBIC0gU0hBMjU2IC0gRzICDDOQIHdhxCbdlFADDTANBgkqhkiG9w0B # AQEFAASCAQClFrPcBCtga/jaOJLK1tLCYzQegUJIpi4g8rX4/PcEV4TV365jen/Y # f6UhrxvDjniZmhXPgt8d5JNtvs0QKGFyMwE+XByu3Lq5SKC70wK7n89joQ0P7S86 # Nxuxs/FaIbDY74I4oz1pR0cwcrc3TBCAc5VdzH1iYZ9Q58ceD+HNYmCNCbpYXxhr # AnFJvMJ6xzB3OEpMbJhQEtjQ+cTxwZeLiJRnTyvBrkuJkE+eP4iIRgmv2rjYxxmZ # 4AQard8xKfHMT/motLV9FmfS9miZ49Zmzm+TBfrmIC1h6JUuHiawEz3mVncOMEDX # fqp02wHNZaGik7CxiM0qyQMVaUiSkjEo # SIG # End signature block |