diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ac9d8d --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +# PowerShell Studio Files +*.temppoint.* +*.psproj.psbuild +*.psbuild + +#VS Code Files +*.vscode + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk \ No newline at end of file diff --git a/Modules/Backup-VCSA.psm1 b/Modules/Backup-VCSA/Backup-VCSA.psm1 similarity index 94% rename from Modules/Backup-VCSA.psm1 rename to Modules/Backup-VCSA/Backup-VCSA.psm1 index 271a7ff..7b790d0 100644 --- a/Modules/Backup-VCSA.psm1 +++ b/Modules/Backup-VCSA/Backup-VCSA.psm1 @@ -32,13 +32,13 @@ If a -LocationType is not chosen, the function will default to FTP. The destination location for a backup must be an empty folder (easiest to use the get-date cmdlet in the location) -ShowProgress will give you a progressbar as well as updates in the console - -SeatBackup will only backup the config whereas -Fullbackup grabs the historical data as well + -CommonBackup will only backup the config whereas -Fullbackup grabs the historical data as well #> param ( [Parameter(ParameterSetName=’FullBackup’)] [switch]$FullBackup, - [Parameter(ParameterSetName=’SeatBackup’)] - [switch]$SeatBackup, + [Parameter(ParameterSetName=’CommonBackup’)] + [switch]$CommonBackup, [ValidateSet('FTPS', 'HTTP', 'SCP', 'HTTPS', 'FTP')] $LocationType = "FTP", $Location, @@ -50,13 +50,14 @@ ) Begin { if (!($global:DefaultCisServers)){ + Add-Type -Assembly System.Windows.Forms [System.Windows.Forms.MessageBox]::Show("It appears you have not created a connection to the CisServer. You will now be prompted to enter your vCenter credentials to continue" , "Connect to CisServer") | out-null $Connection = Connect-CisServer $global:DefaultVIServer } else { $Connection = $global:DefaultCisServers } if ($FullBackup) {$parts = @("common","seat")} - if ($SeatBackup) {$parts = @("seat")} + if ($CommonBackup) {$parts = @("common")} } Process{ $BackupAPI = Get-CisService com.vmware.appliance.recovery.backup.job @@ -72,7 +73,7 @@ $BackupJob = $BackupAPI.create($CreateSpec) } catch { - Write-Error $Error[0].exception.Message + throw $_.Exception.Message } @@ -84,6 +85,7 @@ start-sleep -seconds 5 } until ($BackupAPI.get("$($BackupJob.ID)").progress -eq 100 -or $BackupAPI.get("$($BackupJob.ID)").state -ne "INPROGRESS") + Write-Progress -Activity "Backing up VCSA" -Completed $BackupAPI.get("$($BackupJob.ID)") | select id, progress, state } Else { diff --git a/Modules/ContentLibrary/ContentLibrary.psm1 b/Modules/ContentLibrary/ContentLibrary.psm1 new file mode 100644 index 0000000..318b999 --- /dev/null +++ b/Modules/ContentLibrary/ContentLibrary.psm1 @@ -0,0 +1,699 @@ +Function Get-ContentLibrary { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function lists all available vSphere Content Libaries + .PARAMETER LibraryName + The name of a vSphere Content Library + .EXAMPLE + Get-ContentLibrary + .EXAMPLE + Get-ContentLibrary -LibraryName Test +#> + param( + [Parameter(Mandatory=$false)][String]$LibraryName + ) + + $contentLibaryService = Get-CisService com.vmware.content.library + $libaryIDs = $contentLibaryService.list() + + $results = @() + foreach($libraryID in $libaryIDs) { + $library = $contentLibaryService.get($libraryID) + + # Use vCenter REST API to retrieve name of Datastore that is backing the Content Library + $datastoreService = Get-CisService com.vmware.vcenter.datastore + $datastore = $datastoreService.get($library.storage_backings.datastore_id) + + if($library.publish_info.published) { + $published = $library.publish_info.published + $publishedURL = $library.publish_info.publish_url + $externalReplication = $library.publish_info.persist_json_enabled + } else { + $published = $library.publish_info.published + $publishedURL = "N/A" + $externalReplication = "N/A" + } + + if($library.subscription_info) { + $subscribeURL = $library.subscription_info.subscription_url + $published = "N/A" + } else { + $subscribeURL = "N/A" + } + + if(!$LibraryName) { + $libraryResult = [pscustomobject] @{ + Id = $library.Id; + Name = $library.Name; + Type = $library.Type; + Description = $library.Description; + Datastore = $datastore.name; + Published = $published; + PublishedURL = $publishedURL; + JSONPersistence = $externalReplication; + SubscribedURL = $subscribeURL; + CreationTime = $library.Creation_Time; + } + $results+=$libraryResult + } else { + if($LibraryName -eq $library.name) { + $libraryResult = [pscustomobject] @{ + Name = $library.Name; + Id = $library.Id; + Type = $library.Type; + Description = $library.Description; + Datastore = $datastore.name; + Published = $published; + PublishedURL = $publishedURL; + JSONPersistence = $externalReplication; + SubscribedURL = $subscribeURL; + CreationTime = $library.Creation_Time; + } + $results+=$libraryResult + } + } + } + $results +} + +Function Get-ContentLibraryItems { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function lists all items within a given vSphere Content Library + .PARAMETER LibraryName + The name of a vSphere Content Library + .PARAMETER LibraryItemName + The name of a vSphere Content Library Item + .EXAMPLE + Get-ContentLibraryItems -LibraryName Test + .EXAMPLE + Get-ContentLibraryItems -LibraryName Test -LibraryItemName TinyPhotonVM +#> + param( + [Parameter(Mandatory=$true)][String]$LibraryName, + [Parameter(Mandatory=$false)][String]$LibraryItemName + ) + + $contentLibaryService = Get-CisService com.vmware.content.library + $libaryIDs = $contentLibaryService.list() + + $results = @() + foreach($libraryID in $libaryIDs) { + $library = $contentLibaryService.get($libraryId) + if($library.name -eq $LibraryName) { + $contentLibaryItemService = Get-CisService com.vmware.content.library.item + $itemIds = $contentLibaryItemService.list($libraryID) + + foreach($itemId in $itemIds) { + $item = $contentLibaryItemService.get($itemId) + + if(!$LibraryItemName) { + $itemResult = [pscustomobject] @{ + Name = $item.name; + Id = $item.id; + Description = $item.description; + Size = $item.size + Type = $item.type; + Version = $item.version; + MetadataVersion = $item.metadata_version; + ContentVersion = $item.content_version; + } + $results+=$itemResult + } else { + if($LibraryItemName -eq $item.name) { + $itemResult = [pscustomobject] @{ + Name = $item.name; + Id = $item.id; + Description = $item.description; + Size = $item.size + Type = $item.type; + Version = $item.version; + MetadataVersion = $item.metadata_version; + ContentVersion = $item.content_version; + } + $results+=$itemResult + } + } + } + } + } + $results +} + +Function Get-ContentLibraryItemFiles { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function lists all item files within a given vSphere Content Library + .PARAMETER LibraryName + The name of a vSphere Content Library + .PARAMETER LibraryItemName + The name of a vSphere Content Library Item + .EXAMPLE + Get-ContentLibraryItemFiles -LibraryName Test + .EXAMPLE + Get-ContentLibraryItemFiles -LibraryName Test -LibraryItemName TinyPhotonVM +#> + param( + [Parameter(Mandatory=$true)][String]$LibraryName, + [Parameter(Mandatory=$false)][String]$LibraryItemName + ) + + $contentLibaryService = Get-CisService com.vmware.content.library + $libaryIDs = $contentLibaryService.list() + + $results = @() + foreach($libraryID in $libaryIDs) { + $library = $contentLibaryService.get($libraryId) + if($library.name -eq $LibraryName) { + $contentLibaryItemService = Get-CisService com.vmware.content.library.item + $itemIds = $contentLibaryItemService.list($libraryID) + + foreach($itemId in $itemIds) { + $itemName = ($contentLibaryItemService.get($itemId)).name + $contenLibraryItemFileSerice = Get-CisService com.vmware.content.library.item.file + $files = $contenLibraryItemFileSerice.list($itemId) + + foreach($file in $files) { + if(!$LibraryItemName) { + $fileResult = [pscustomobject] @{ + Name = $file.name; + Version = $file.version; + Size = $file.size; + Stored = $file.cached; + } + $results+=$fileResult + } else { + if($itemName -eq $LibraryItemName) { + $fileResult = [pscustomobject] @{ + Name = $file.name; + Version = $file.version; + Size = $file.size; + Stored = $file.cached; + } + $results+=$fileResult + } + } + } + } + } + } + $results +} + +Function Set-ContentLibrary { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function updates the JSON Persistence property for a given Content Library + .PARAMETER LibraryName + The name of a vSphere Content Library + .EXAMPLE + Set-ContentLibraryItems -LibraryName Test -JSONPersistenceEnabled + .EXAMPLE + Set-ContentLibraryItems -LibraryName Test -JSONPersistenceDisabled +#> + param( + [Parameter(Mandatory=$true)][String]$LibraryName, + [Parameter(Mandatory=$false)][Switch]$JSONPersistenceEnabled, + [Parameter(Mandatory=$false)][Switch]$JSONPersistenceDisabled + ) + + $contentLibaryService = Get-CisService com.vmware.content.library + $libaryIDs = $contentLibaryService.list() + + $found = $false + foreach($libraryID in $libaryIDs) { + $library = $contentLibaryService.get($libraryId) + if($library.name -eq $LibraryName) { + $found = $true + break + } + } + + if($found) { + $localLibraryService = Get-CisService -Name "com.vmware.content.local_library" + + if($JSONPersistenceEnabled) { + $jsonPersist = $true + } else { + $jsonPersist = $false + } + + $updateSpec = $localLibraryService.Help.update.update_spec.Create() + $updateSpec.type = $library.type + $updateSpec.publish_info.authentication_method = $library.publish_info.authentication_method + $updateSpec.publish_info.persist_json_enabled = $jsonPersist + Write-Host "Updating JSON Persistence configuration setting for $LibraryName ..." + $localLibraryService.update($library.id,$updateSpec) + } else { + Write-Host "Unable to find Content Library $Libraryname" + } +} + +Function New-ExtReplicatedContentLibrary { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function creates a new Subscriber Content Library from a JSON Persisted + Content Library that has been externally replicated + .PARAMETER LibraryName + The name of the new vSphere Content Library + .PARAMETER DatastoreName + The name of the vSphere Datastore which contains JSON Persisted configuration file + .PARAMETER SubscribeLibraryName + The name fo the root directroy of the externally replicated Content Library residing on vSphere Datastore + .PARAMETER AutoSync + Whether or not to Automatically sync content + .PARAMETER OnDemand + Only sync content when requested + .EXAMPLE + New-ExtReplicatedContentLibrary -LibraryName Bar -DatastoreName iSCSI-02 -SubscribeLibraryName myExtReplicatedLibrary + .EXAMPLE + New-ExtReplicatedContentLibrary -LibraryName Bar -DatastoreName iSCSI-02 -SubscribeLibraryName myExtReplicatedLibrary -AutoSync $false -OnDemand $true +#> + param( + [Parameter(Mandatory=$true)][String]$LibraryName, + [Parameter(Mandatory=$true)][String]$DatastoreName, + [Parameter(Mandatory=$true)][String]$SubscribeLibraryName, + [Parameter(Mandatory=$false)][Boolean]$AutoSync=$false, + [Parameter(Mandatory=$false)][Boolean]$OnDemand=$true + ) + + $datastore = Get-Datastore -Name $DatastoreName + + if($datastore) { + $datastoreId = $datastore.ExtensionData.MoRef.Value + $datastoreUrl = $datastore.ExtensionData.Info.Url + $subscribeUrl = $datastoreUrl + $SubscribeLibraryName + "/lib.json" + + $subscribeLibraryService = Get-CisService -Name "com.vmware.content.subscribed_library" + + $StorageSpec = [pscustomobject] @{ + datastore_id = $datastoreId; + type = "DATASTORE"; + } + + $UniqueChangeId = [guid]::NewGuid().tostring() + + $createSpec = $subscribeLibraryService.Help.create.create_spec.Create() + $createSpec.name = $LibraryName + $addResults = $createSpec.storage_backings.Add($StorageSpec) + $createSpec.subscription_info.automatic_sync_enabled = $false + $createSpec.subscription_info.on_demand = $true + $createSpec.subscription_info.subscription_url = $subscribeUrl + $createSpec.subscription_info.authentication_method = "NONE" + $createSpec.type = "SUBSCRIBED" + Write-Host "Creating new Externally Replicated Content Library called $LibraryName ..." + $library = $subscribeLibraryService.create($UniqueChangeId,$createSpec) + } +} + +Function Remove-SubscribedContentLibrary { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function deletes a Subscriber Content Library + .PARAMETER LibraryName + The name of the new vSphere Content Library to delete + .EXAMPLE + Remove-SubscribedContentLibrary -LibraryName Bar +#> + param( + [Parameter(Mandatory=$true)][String]$LibraryName + ) + + $contentLibaryService = Get-CisService com.vmware.content.library + $libaryIDs = $contentLibaryService.list() + + $found = $false + foreach($libraryID in $libaryIDs) { + $library = $contentLibaryService.get($libraryId) + if($library.name -eq $LibraryName) { + $found = $true + break + } + } + + if($found) { + $subscribeLibraryService = Get-CisService -Name "com.vmware.content.subscribed_library" + + Write-Host "Deleting Subscribed Content Library $LibraryName ..." + $subscribeLibraryService.delete($library.id) + } else { + Write-Host "Unable to find Content Library $LibraryName" + } +} + +Function New-LocalContentLibrary { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function creates a new Subscriber Content Library from a JSON Persisted + Content Library that has been externally replicated + .PARAMETER LibraryName + The name of the new vSphere Content Library + .PARAMETER DatastoreName + The name of the vSphere Datastore to store the Content Library + .PARAMETER Publish + Whther or not to publish the Content Library, this is required for JSON Peristence + .PARAMETER JSONPersistence + Whether or not to enable JSON Persistence which enables external replication of Content Library + .EXAMPLE + New-LocalContentLibrary -LibraryName Foo -DatastoreName iSCSI-01 -Publish $true + .EXAMPLE + New-LocalContentLibrary -LibraryName Foo -DatastoreName iSCSI-01 -Publish $true -JSONPersistence $true +#> + param( + [Parameter(Mandatory=$true)][String]$LibraryName, + [Parameter(Mandatory=$true)][String]$DatastoreName, + [Parameter(Mandatory=$false)][Boolean]$Publish=$true, + [Parameter(Mandatory=$false)][Boolean]$JSONPersistence=$false + ) + + $datastore = Get-Datastore -Name $DatastoreName + + if($datastore) { + $datastoreId = $datastore.ExtensionData.MoRef.Value + $localLibraryService = Get-CisService -Name "com.vmware.content.local_library" + + $StorageSpec = [pscustomobject] @{ + datastore_id = $datastoreId; + type = "DATASTORE"; + } + + $UniqueChangeId = [guid]::NewGuid().tostring() + + $createSpec = $localLibraryService.Help.create.create_spec.Create() + $createSpec.name = $LibraryName + $addResults = $createSpec.storage_backings.Add($StorageSpec) + $createSpec.publish_info.authentication_method = "NONE" + $createSpec.publish_info.persist_json_enabled = $JSONPersistence + $createSpec.publish_info.published = $Publish + $createSpec.type = "LOCAL" + Write-Host "Creating new Local Content Library called $LibraryName ..." + $library = $localLibraryService.create($UniqueChangeId,$createSpec) + } +} + +Function Remove-LocalContentLibrary { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function deletes a Local Content Library + .PARAMETER LibraryName + The name of the new vSphere Content Library to delete + .EXAMPLE + Remove-LocalContentLibrary -LibraryName Bar +#> + param( + [Parameter(Mandatory=$true)][String]$LibraryName + ) + + $contentLibaryService = Get-CisService com.vmware.content.library + $libaryIDs = $contentLibaryService.list() + + $found = $false + foreach($libraryID in $libaryIDs) { + $library = $contentLibaryService.get($libraryId) + if($library.name -eq $LibraryName) { + $found = $true + break + } + } + + if($found) { + $localLibraryService = Get-CisService -Name "com.vmware.content.local_library" + + Write-Host "Deleting Local Content Library $LibraryName ..." + $localLibraryService.delete($library.id) + } else { + Write-Host "Unable to find Content Library $LibraryName" + } +} + +Function Copy-ContentLibrary { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function copies all library items from one Content Library to another + .PARAMETER SourceLibaryName + The name of the source Content Library to copy from + .PARAMETER DestinationLibaryName + The name of the desintation Content Library to copy to + .PARAMETER DeleteSourceFile + Whther or not to delete library item from the source Content Library after copy + .EXAMPLE + Copy-ContentLibrary -SourceLibaryName Foo -DestinationLibaryName Bar + .EXAMPLE + Copy-ContentLibrary -SourceLibaryName Foo -DestinationLibaryName Bar -DeleteSourceFile $true +#> + param( + [Parameter(Mandatory=$true)][String]$SourceLibaryName, + [Parameter(Mandatory=$true)][String]$DestinationLibaryName, + [Parameter(Mandatory=$false)][Boolean]$DeleteSourceFile=$false + ) + + $sourceLibraryId = (Get-ContentLibrary -LibraryName $SourceLibaryName).Id + if($sourceLibraryId -eq $null) { + Write-Host -ForegroundColor red "Unable to find Source Content Library named $SourceLibaryName" + exit + } + $destinationLibraryId = (Get-ContentLibrary -LibraryName $DestinationLibaryName).Id + if($destinationLibraryId -eq $null) { + Write-Host -ForegroundColor Red "Unable to find Destination Content Library named $DestinationLibaryName" + break + } + + $sourceItemFiles = Get-ContentLibraryItems -LibraryName $SourceLibaryName + if($sourceItemFiles -eq $null) { + Write-Host -ForegroundColor red "Unable to retrieve Content Library Items from $SourceLibaryName" + break + } + + $contentLibaryItemService = Get-CisService com.vmware.content.library.item + + foreach ($sourceItemFile in $sourceItemFiles) { + # Check to see if file already exists in destination Content Library + $result = Get-ContentLibraryItems -LibraryName $DestinationLibaryName -LibraryItemName $sourceItemFile.Name + + if($result -eq $null) { + # Create CopySpec + $copySpec = $contentLibaryItemService.Help.copy.destination_create_spec.Create() + $copySpec.library_id = $destinationLibraryId + $copySpec.name = $sourceItemFile.Name + $copySpec.description = $sourceItemFile.Description + # Create random Unique Copy Id + $UniqueChangeId = [guid]::NewGuid().tostring() + + # Perform Copy + try { + Write-Host -ForegroundColor Cyan "Copying" $sourceItemFile.Name "..." + $copyResult = $contentLibaryItemService.copy($UniqueChangeId, $sourceItemFile.Id, $copySpec) + } catch { + Write-Host -ForegroundColor Red "Failed to copy" $sourceItemFile.Name + $Error[0] + break + } + + # Delete source file if set to true + if($DeleteSourceFile) { + try { + Write-Host -ForegroundColor Magenta "Deleteing" $sourceItemFile.Name "..." + $deleteResult = $contentLibaryItemService.delete($sourceItemFile.Id) + } catch { + Write-Host -ForegroundColor Red "Failed to delete" $sourceItemFile.Name + $Error[0] + break + } + } + } else { + Write-Host -ForegroundColor Yellow "Skipping" $sourceItemFile.Name "already exists" + + # Delete source file if set to true + if($DeleteSourceFile) { + try { + Write-Host -ForegroundColor Magenta "Deleteing" $sourceItemFile.Name "..." + $deleteResult = $contentLibaryItemService.delete($sourceItemFile.Id) + } catch { + Write-Host -ForegroundColor Red "Failed to delete" $sourceItemFile.Name + break + } + } + } + } +} + +Function New-VMTX { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function clones a VM to VM Template in Content Library (currently only supported on VMC) + .PARAMETER SourceVMName + The name of the source VM to clone + .PARAMETER VMTXName + The name of the VM Template in Content Library + .PARAMETER Description + Description of the VM template + .PARAMETER LibaryName + The name of the Content Library to clone to + .PARAMETER FolderName + The name of vSphere Folder (Defaults to Workloads for VMC) + .PARAMETER ResourcePoolName + The name of the vSphere Resource Pool (Defaults to Compute-ResourcePools for VMC) + .EXAMPLE + New-VMTX -SourceVMName "Windows10-BaseInstall" -VMTXName "Windows10-VMTX-Template" -LibraryName "VMC-CL-01" +#> + param( + [Parameter(Mandatory=$true)][String]$SourceVMName, + [Parameter(Mandatory=$true)][String]$VMTXName, + [Parameter(Mandatory=$false)][String]$Description, + [Parameter(Mandatory=$true)][String]$LibraryName, + [Parameter(Mandatory=$false)][String]$FolderName="Workloads", + [Parameter(Mandatory=$false)][String]$ResourcePoolName="Compute-ResourcePool" + ) + + $vmtxService = Get-CisService -Name "com.vmware.vcenter.vm_template.library_items" + + $sourceVMId = ((Get-VM -Name $SourceVMName).ExtensionData.MoRef).Value + $libraryId = ((Get-ContentLibrary -LibraryName $LibraryName).Id).Value + $folderId = ((Get-Folder -Name $FolderName).ExtensionData.MoRef).Value + $rpId = ((Get-ResourcePool -Name $ResourcePoolName).ExtensionData.MoRef).Value + + $vmtxCreateSpec = $vmtxService.Help.create.spec.Create() + $vmtxCreateSpec.source_vm = $sourceVMId + $vmtxCreateSpec.name = $VMTXName + $vmtxCreateSpec.description = $Description + $vmtxCreateSpec.library = $libraryId + $vmtxCreateSpec.placement.folder = $folderId + $vmtxCreateSpec.placement.resource_pool = $rpId + + Write-Host "`nCreating new VMTX Template from $SourceVMName in Content Library $LibraryName ..." + $result = $vmtxService.create($vmtxCreateSpec) +} + +Function New-VMFromVMTX { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function deploys a new VM from Template in Content Library (currently only supported in VMC) + .PARAMETER VMTXName + The name of the VM Template in Content Library to deploy from + .PARAMETER NewVMName + The name of the new VM to deploy + .PARAMETER FolderName + The name of vSphere Folder (Defaults to Workloads for VMC) + .PARAMETER ResourcePoolName + The name of the vSphere Resource Pool (Defaults to Compute-ResourcePools for VMC) + .PARAMETER NumCpu + The number of vCPU to configure for the new VM + .PARAMETER MemoryMb + The amount of memory (MB) to configure for the new VM + .PARAMETER PowerOn + To power on the VM after deploy + .EXAMPLE + New-VMFromVMTX -NewVMName "FooFoo" -VMTXName "FooBar" -PowerOn $true -NumCpu 4 -MemoryMB 2048 +#> + param( + [Parameter(Mandatory=$true)][String]$VMTXName, + [Parameter(Mandatory=$true)][String]$NewVMName, + [Parameter(Mandatory=$false)][String]$FolderName="Workloads", + [Parameter(Mandatory=$false)][String]$ResourcePoolName="Compute-ResourcePool", + [Parameter(Mandatory=$false)][String]$DatastoreName="WorkloadDatastore", + [Parameter(Mandatory=$false)][Int]$NumCpu, + [Parameter(Mandatory=$false)][Int]$MemoryMB, + [Parameter(Mandatory=$false)][Boolean]$PowerOn=$false + ) + + $vmtxService = Get-CisService -Name "com.vmware.vcenter.vm_template.library_items" + $vmtxId = (Get-ContentLibraryItem -Name $VMTXName).Id + $folderId = ((Get-Folder -Name $FolderName).ExtensionData.MoRef).Value + $rpId = ((Get-ResourcePool -Name $ResourcePoolName).ExtensionData.MoRef).Value + $datastoreId = ((Get-Datastore -Name $DatastoreName).ExtensionData.MoRef).Value + + $vmtxDeploySpec = $vmtxService.Help.deploy.spec.Create() + $vmtxDeploySpec.name = $NewVMName + $vmtxDeploySpec.powered_on = $PowerOn + $vmtxDeploySpec.placement.folder = $folderId + $vmtxDeploySpec.placement.resource_pool = $rpId + $vmtxDeploySpec.vm_home_storage.datastore = $datastoreId + $vmtxDeploySpec.disk_storage.datastore = $datastoreId + + if($NumCpu) { + $vmtxDeploySpec.hardware_customization.cpu_update.num_cpus = $NumCpu + } + if($MemoryMB) { + $vmtxDeploySpec.hardware_customization.memory_update.memory = $MemoryMB + } + + Write-Host "`nDeploying new VM $NewVMName from VMTX Template $VMTXName ..." + $results = $vmtxService.deploy($vmtxId,$vmtxDeploySpec) +} \ No newline at end of file diff --git a/Modules/CrossvCentervmotion/XVM.psm1 b/Modules/CrossvCentervmotion/XVM.psm1 new file mode 100644 index 0000000..cb9d225 --- /dev/null +++ b/Modules/CrossvCentervmotion/XVM.psm1 @@ -0,0 +1,298 @@ +Function Get-XVCMStatus { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function returns whether Cross vCenter Workload Migration Utility is running or not + .EXAMPLE + Get-XVCMStatus +#> + $Uri = "http://localhost:8080/api/status" #Updated for 2.0, Old: "http://localhost:8080/api/ping" + + $results = Invoke-WebRequest -Uri $Uri -Method GET -TimeoutSec 5 + + if($results.StatusCode -eq 200) { + Write-Host -ForegroundColor Green $results.Content + } else { Write-Host -ForegroundColor Red "Cross vCenter Workload Migration Utility is probably not running" } +} + +Function Get-XVCMSite { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function returns all registered vCenter Servers + .EXAMPLE + Get-XVCMSite +#> + $Uri = "http://localhost:8080/api/sites" + + $results = Invoke-WebRequest -Uri $Uri -Method GET + + if($results.StatusCode -eq 200) { + ($results.Content | ConvertFrom-Json)|select sitename,hostname,username + } else { Write-Host -ForegroundColor Red "Failed to retrieve VC Site Registration details" } +} + +Function New-XVCMSite { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function registers a new vCenter Server endpoint + .PARAMETER SiteName + The display name for the particular vCenter Server to be registered + .PARAMETER VCHostname + The Hostname/IP Address of vCenter Server + .PARAMETER VCUsername + The VC Username of vCenter Server + .PARAMETER VCPassword + The VC Password of vCenter Server + .PARAMETER Insecure + Flag to disable SSL Verification checking, useful for lab environments + .EXAMPLE + New-XVCMSite -SiteName "SiteA" -VCHostname "vcenter65-1.primp-industries.com" -VCUsername "administrator@vsphere.local" -VCPassword "VMware1!" -Insecure +#> + param( + [Parameter(Mandatory=$true)][String]$SiteName, + [Parameter(Mandatory=$true)][String]$VCHostname, + [Parameter(Mandatory=$true)][String]$VCUsername, + [Parameter(Mandatory=$true)][String]$VCPassword, + [Parameter(Mandatory=$false)][Switch]$Insecure + ) + + $Uri = "http://localhost:8080/api/sites" + + $insecureFlag = $false + if($Insecure) { + $insecureFlag = $true + } + + $body = @{ + "sitename"=$SiteName; + "hostname"=$VCHostname; + "username"=$VCUsername; + "password"=$VCPassword; + "insecure"=$insecureFlag; + } + + $body = $body | ConvertTo-Json + + Write-Host -ForegroundColor Cyan "Registering vCenter Server $VCHostname as $SiteName ..." + $results = Invoke-WebRequest -Uri $Uri -Method POST -Body $body -ContentType "application/json" + + if($results.StatusCode -eq 200) { + Write-Host -ForegroundColor Green "Successfully registered $SiteName" + } else { Write-Host -ForegroundColor Red "Failed to register $SiteName" } +} + +Function Remove-XVCMSite { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function removes vCenter Server endpoint + .PARAMETER SiteName + The name of the registered vCenter Server to remove + .EXAMPLE + Remove-XVCMSite -SiteName "SiteA" +#> + param( + [Parameter(Mandatory=$true)][String]$SiteName + ) + + $Uri = "http://localhost:8080/api/sites/$SiteName" + + Write-Host -ForegroundColor Cyan "Deleting vCenter Server Site Registerion $SiteName ..." + $results = Invoke-WebRequest -Uri $Uri -Method DELETE + + if($results.StatusCode -eq 200) { + Write-Host -ForegroundColor Green "Successfully deleted $SiteName" + } else { Write-Host -ForegroundColor Red "Failed to deleted $SiteName" } +} + +Function New-XVCMRequest { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function initiates a migration request + .PARAMETER opType + The type of task, Relocate or Clone + .PARAMETER SrcSite + The name of the source vCenter Server + .PARAMETER DstSite + The name of the destination vCenter Server + .PARAMETER SrcDatacenter + The name of the source vSphere Datacenter + .PARAMETER DstDatacenter + The name of the destination vSphere Datacenter + .PARAMETER SrcCluster + + .PARAMETER DstCluster + The name of the destination vSphere Cluster, set to null if DstHost is defined + .PARAMETER DstDatastore + The name of the destination Datastore + .PARAMETER DstHost + The name of the destination host. Set to null if DstCluster is defined + .PARAMETER srcVMs + List of VMs to migrate + .PARAMETER NetworkMapping + Hash table of the VM network mappings between your source and destination vCenter Server + .EXAMPLE + New-XVCMRequest -opType Relocate -SrcSite SiteA -DstSite SiteB ` + -SrcDatacenter Datacenter-SiteA -DstDatacenter Datacenter-SiteB ` + -DstCluster $null -DstHost VMhost1.test.lab ` + -DstDatastore vsanDatastore ` + -srcVMs @("PhotonOS-01","PhotonOS-02","PhotonOS-03","PhotonOS-04") ` + -NetworkMapping @{"DVPG-VM Network 1"="DVPG-Internal Network";"DVPG-VM Network 2"="DVPG-External Network"} +#> + param( + [Parameter(Mandatory=$true)][String]$opType, #Added by CPM for 2.0 + [Parameter(Mandatory=$true)][String]$SrcSite, + [Parameter(Mandatory=$true)][String]$DstSite, + [Parameter(Mandatory=$true)][String]$SrcDatacenter, + [Parameter(Mandatory=$true)][String]$DstDatacenter, + #[Parameter(Mandatory=$true)][String]$SrcCluster, #Removed by CPM for 2.0 + [Parameter(Mandatory=$true)][AllowNull()] $DstCluster, #Added [AllowNull()], removed [String] by CPM for 2.0 + [Parameter(Mandatory=$true)][String]$DstDatastore, + [Parameter(Mandatory=$true)][AllowNull()] $DstHost, #Added by CPM for 2.0 + [Parameter(Mandatory=$true)][String[]]$srcVMs, + [Parameter(Mandatory=$true)][Hashtable]$NetworkMapping + ) + + $Uri = "http://localhost:8080/api/tasks" + + $body = @{ + "sourceSite"=$SrcSite; + "targetSite"=$DstSite; + "sourceDatacenter"=$SrcDatacenter; + "targetDatacenter"=$dstDatacenter; + #"sourceCluster"=$SrcCluster; #Removed by CPM for 2.0 + "targetCluster"=$DstCluster; + "targetDatastore"=$DstDatastore; + "targetHost"=$DstHost; #Added by CPM for 2.0 + "networkMap"=$NetworkMapping; + "vmList"=$srcVMs; + "operationType"=$opType; #Added by CPM for 2.0 + } + + $body = $body | ConvertTo-Json + + Write-Host -ForegroundColor Cyan "Initiating migration request ..." + $results = Invoke-WebRequest -Uri $Uri -Method POST -Body $body -ContentType "application/json" + + if($results.StatusCode -eq 200) { + $taskId = ($results.Content | ConvertFrom-Json).requestId + Write-Host -ForegroundColor Green "Successfully issued migration with TaskID: $taskId" + } else { Write-Host -ForegroundColor Red "Failed to initiate migration request" } +} + +Function Get-XVCMTask { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function retrieves either all migration tasks and/or a specific migration task + .PARAMETER Id + The task ID returned from initiating a migration + .EXAMPLE + Get-XVCMTask -Id +#> + param( + [Parameter(Mandatory=$false)][String]$Id + ) + + $Uri = "http://localhost:8080/api/tasks" + + if($Id) { + $body = @{"requestId"=$Id} + + $results = Invoke-WebRequest -Uri $Uri -Method GET -Body $body -ContentType "application/json" + } else { + $results = Invoke-WebRequest -Uri $Uri -Method GET + } + + if($results.StatusCode -eq 200) { + $results.Content | ConvertFrom-Json + } else { Write-Host -ForegroundColor Red "Failed to retrieve tasks" } +} + +Function Get-VMNetwork { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function returns the list of all VM Networks attached to + given VMs to help with initiating migration + .PARAMETER srcVMs + List of VMs to query their current VM Networks + .EXAMPLE + Get-VMNetwork -srcVMs @("PhotonOS-01","PhotonOS-02","PhotonOS-03","PhotonOS-04") +#> + param( + [Parameter(Mandatory=$false)][String[]]$srcVMs + ) + + if (-not $global:DefaultVIServers) { Write-Host -ForegroundColor red "No vCenter Server Connection found, please connect to your source vCenter Server using Connect-VIServer"; break } + + $results = @() + if($srcVMs) { + foreach ($srcVM in $srcVMs) { + $vm = Get-VM -Name $srcVM + $networkDetails = $vm | Get-NetworkAdapter + $tmp = [pscustomobject] @{ + Name = $srcVM; + Adapter = $networkDetails.name; + Network = $networkDetails.NetworkName; + } + $results+=$tmp + } + } else { + foreach ($vm in Get-VM) { + $networkDetails = $vm | Get-NetworkAdapter + $tmp = [pscustomobject] @{ + Name = $vm.Name; + Adapter = $networkDetails.name; + Network = $networkDetails.NetworkName; + } + $results+=$tmp + } + } + $results +} diff --git a/Modules/DatastoreFunctions/DatastoreFunctions.psm1 b/Modules/DatastoreFunctions/DatastoreFunctions.psm1 new file mode 100644 index 0000000..b908a40 --- /dev/null +++ b/Modules/DatastoreFunctions/DatastoreFunctions.psm1 @@ -0,0 +1,245 @@ +<# +.SYNOPSIS Datastore Functions +.DESCRIPTION A collection of functions to manipulate datastore Mount + Attach status +.EXAMPLE Get-Datastore | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize +.EXAMPLE Get-Datastore IX2ISCSI01 | Unmount-Datastore +.EXAMPLE Get-Datastore IX2ISCSI01 | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize +.EXAMPLE Get-Datastore IX2iSCSI01 | Mount-Datastore +.EXAMPLE Get-Datastore IX2iSCSI01 | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize +.EXAMPLE Get-Datastore IX2iSCSI01 | Detach-Datastore +.EXAMPLE Get-Datastore IX2iSCSI01 | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize +.EXAMPLE Get-Datastore IX2iSCSI01 | Attach-datastore +.EXAMPLE Get-Datastore IX2iSCSI01 | Get-DatastoreMountInfo | Sort Datastore, VMHost | FT -AutoSize +.NOTES Written by Alan Renouf, originally published at https://blogs.vmware.com/vsphere/2012/01/automating-datastore-storage-device-detachment-in-vsphere-5.html +.NOTES May 2017: Modified by Jason Coleman (virtuallyjason.blogspot.com), to improve performance when dealing with a large number of hosts and datastores +#> +Function Get-HostViews { + [CmdletBinding()] + Param ( + $Datastore + ) + Begin{ + $allDatastores = @() + } + Process { + $allDatastores += $Datastore + } + End { + #Build the array of Datastore Objects + if (-not $Datastore) { + $allDatastores = Get-Datastore + } + $allDatastores = $allDatastores | ? {$_.pstypenames -contains "VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl"} + if (-not $allDatastores){ + Throw "No Datastores found.`nIs ""$Datastore"" a Datastore Object?" + } + $allHosts = @() + $DShostsKeys = $allDatastores.extensiondata.host.key.value | sort | get-unique -asstring + $DShosts = foreach ($thisKey in $DShostsKeys) {($allDatastores.extensiondata.host | ? {$_.key.value -eq $thisKey})[0]} + $i = 1 + foreach ($DSHost in $DSHosts){ + write-progress -activity "Collecting ESXi Host Views" -status "Querying $($dshost.key)..." -percentComplete ($i++/$DSHosts.count*100) + $hostObj = "" | select keyValue,hostView,storageSys + $hostObj.hostView = get-view $DSHost.key + $hostObj.keyValue = $DSHost.key.value + $hostObj.storageSys = Get-View $hostObj.hostView.ConfigManager.StorageSystem + $allHosts += $hostObj + } + write-progress -activity "Collecting ESXi Host Views" -completed + $allHosts + } +} + +Function Get-DatastoreMountInfo { + [CmdletBinding()] + Param ( + [Parameter(ValueFromPipeline=$true)] + $Datastore + ) + #Roll back up an unrolled array from a pipeline + Begin{ + $allDatastores = @() + } + Process { + $allDatastores += $Datastore + } + End { + $AllInfo = @() + #Build the array of Datastore Objects + if (-not $Datastore) { + $allDatastores = Get-Datastore + } + $allDatastores = $allDatastores | ? {$_.pstypenames -contains "VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl"} + if (-not $allDatastores){ + Throw "No Datastores found.`nIs ""$Datastore"" a Datastore Object?" + } + $allDatastoreNAAs = foreach ($ds in $allDatastores) {$ds.ExtensionData.Info.vmfs.extent[0].diskname} + + #Build the array of custom Host Objects + $allHosts = Get-HostViews -datastore $allDatastores + $output = @() + $i = 1 + foreach ($dsHost in $allHosts){ + write-progress -activity "Checking Datastore access" -status "Checking $($dshost.hostview.name)..." -percentComplete ($i++ / $allHosts.count * 100) + #Get all devices on the host that match the list of $allDatastoreNAAs + $devices = $dsHost.storagesys.StorageDeviceInfo.ScsiLun + foreach ($device in $devices){ + if ($allDatastoreNAAs -contains $device.canonicalName){ + #Record information about this device/host combo + $thisDatastore = $alldatastores | ? {$_.ExtensionData.Info.vmfs.extent[0].diskname -eq $device.canonicalName} + $hostviewDSAttachState = "" + if ($device.operationalState[0] -eq "ok") { + $hostviewDSAttachState = "Attached" + } elseif ($device.operationalState[0] -eq "off") { + $hostviewDSAttachState = "Detached" + } else { + $hostviewDSAttachState = $device.operationalstate[0] + } + $Info = "" | Select Datastore, VMHost, Lun, Mounted, State + $Info.VMHost = $dsHost.hostview.name + $Info.Datastore = $thisDatastore.name + $Info.Lun = $device.canonicalName + $Info.mounted = ($thisDatastore.extensiondata.host | ? {$_.key.value -eq $dshost.keyvalue}).mountinfo.mounted + $Info.state = $hostviewDSAttachState + $output += $info + } + } + } + write-progress -activity "Checking Datastore access" -completed + $output + } +} + +Function Detach-Datastore { + [CmdletBinding()] + Param ( + [Parameter(ValueFromPipeline=$true)] + $Datastore + ) + Begin{ + $allDatastores = @() + } + Process { + $allDatastores += $Datastore + } + End { + $allDatastores = $allDatastores | ? {$_.pstypenames -contains "VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl"} + if (-not $allDatastores){ + Throw "No Datastores found.`nIs ""$Datastore"" a Datastore Object?" + } + $allDatastoreNAAs = foreach ($ds in $allDatastores) {$ds.ExtensionData.Info.vmfs.extent[0].diskname} + $allHosts = Get-HostViews -datastore $allDatastores + $j = 1 + foreach ($dsHost in $allHosts){ + #Get all devices on the host that match the list of $allDatastoreNAAs + write-progress -id 1 -activity "Detaching Datastores" -status "Removing device(s) from $($dsHost.hostview.name)" -percentComplete ($j++ / $allHosts.count * 100) + $devices = $dsHost.storagesys.StorageDeviceInfo.ScsiLun | ? {$allDatastoreNAAs -contains $_.canonicalName} + $i = 1 + foreach ($device in $devices){ + write-progress -parentid 1 -activity "Detaching Datastores" -status "Removing device: $(($allDatastores | ? {$_.ExtensionData.Info.vmfs.extent[0].diskname -eq $device.canonicalName}).name)" -percentComplete ($i++ / $allDatastoreNAAs.count * 100) + $LunUUID = $Device.Uuid + $dsHost.storageSys.DetachScsiLun($LunUUID); + } + } + write-progress -activity "Detaching Datastores" -completed + } +} + +Function Attach-Datastore { + [CmdletBinding()] + Param ( + [Parameter(ValueFromPipeline=$true)] + $Datastore + ) + Begin{ + $allDatastores = @() + } + Process { + $allDatastores += $Datastore + } + End { + $allDatastores = $allDatastores | ? {$_.pstypenames -contains "VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl"} + if (-not $allDatastores){ + Throw "No Datastores found.`nIs ""$Datastore"" a Datastore Object?" + } + $allDatastoreNAAs = foreach ($ds in $allDatastores) {$ds.ExtensionData.Info.vmfs.extent[0].diskname} + $allHosts = Get-HostViews -datastore $allDatastores + $j = 1 + foreach ($dsHost in $allHosts){ + #Get all devices on the host that match the list of $allDatastoreNAAs + write-progress -id 1 -activity "Attaching Datastores" -status "Attaching devices to $($dsHost.hostview.name)" -percentComplete ($j++ / $allHosts.count * 100) + $devices = $dsHost.storagesys.StorageDeviceInfo.ScsiLun + $i = 1 + foreach ($device in $devices){ + write-progress -parentid 1 -activity "Attaching Datastores" -status "Attaching device: $($Device.Uuid)" -percentComplete ($i++ / $devices.count * 100) + if ($allDatastoreNAAs -contains $device.canonicalName){ + $LunUUID = $Device.Uuid + $dsHost.storageSys.AttachScsiLun($LunUUID); + } + } + } + write-progress -activity "Attaching Datastores" -completed + } +} + +Function Unmount-Datastore { + [CmdletBinding()] + Param ( + [Parameter(ValueFromPipeline=$true)] + $Datastore + ) + Begin{ + $allDatastores = @() + } + Process { + $allDatastores += $Datastore + } + End { + $allDatastores = $allDatastores | ? {$_.pstypenames -contains "VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl"} + if (-not $allDatastores){ + Throw "No Datastores found.`nIs ""$Datastore"" a Datastore Object?" + } + $allHosts = Get-HostViews -datastore $allDatastores + $j = 1 + foreach ($dsHost in $allHosts){ + write-progress -id 1 -activity "Unmounting Datastores" -status "Unmounting devices from $($dsHost.hostview.name)" -percentComplete ($j++ / $allHosts.count * 100) + $i = 1 + foreach ($ds in $allDatastores){ + write-progress -parentid 1 -activity "Unmounting Datastores" -status "Unmounting device: $($ds.name)" -percentComplete ($i++ / $allDatastores.count * 100) + $dsHost.storageSys.UnmountVmfsVolume($DS.ExtensionData.Info.vmfs.uuid); + } + } + write-progress -activity "Unmounting Datastores" -completed + } +} + +Function Mount-Datastore { + [CmdletBinding()] + Param ( + [Parameter(ValueFromPipeline=$true)] + $Datastore + ) + Begin{ + $allDatastores = @() + } + Process { + $allDatastores += $Datastore + } + End { + $allDatastores = $allDatastores | ? {$_.pstypenames -contains "VMware.VimAutomation.ViCore.Impl.V1.DatastoreManagement.DatastoreImpl"} + if (-not $allDatastores){ + Throw "No Datastores found.`nIs ""$Datastore"" a Datastore Object?" + } + $allHosts = Get-HostViews -datastore $allDatastores + $j = 0 + foreach ($dsHost in $allHosts){ + write-progress -activity "Mounting Datastores" -status "Mounting devices to $($dsHost.hostview.name)" -percentComplete ($j++ / $allHosts.count * 100) + $i = 1 + foreach ($ds in $allDatastores){ + write-progress -activity "Mounting Datastores" -status "Mounting device: $($DS.ExtensionData.Info.vmfs.uuid)" -percentComplete ($i++ / $allDatastores.count * 100) + $dsHost.storageSys.MountVmfsVolume($DS.ExtensionData.Info.vmfs.uuid); + } + } + write-progress -activity "Mounting Datastores" -completed + } +} diff --git a/Modules/Get-NICDetails/Get-NICDetails.psm1 b/Modules/Get-NICDetails/Get-NICDetails.psm1 new file mode 100644 index 0000000..30f1440 --- /dev/null +++ b/Modules/Get-NICDetails/Get-NICDetails.psm1 @@ -0,0 +1,93 @@ +function Get-NICDetails { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 2017.02 ver 1.0 Base Release + =========================================================================== + External Code Sources: + - + =========================================================================== + Tested Against Environment: + vSphere Version: ESXi 6.0 U2, ESXi 6.5 + PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1 + PowerShell Version: 4.0, 5.0 + OS Version: Windows 8.1, Server 2008 R2, Server 2012 R2 + Keyword: ESXi, NIC, vmnic, Driver, Firmware + =========================================================================== + + .DESCRIPTION + Reports Firmware and Driver Details for your ESXi vmnics. + + .Example + Get-NICDetails -Clustername * + + .PARAMETER Clustername + Name or Wildcard of your vSphere Cluster Name to process. + + +#Requires PS -Version 4.0 +#Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0)] + [ValidateNotNullorEmpty()] + [String] $Clustername + +) + +Begin { + $Validate = $True + + if (($myCluster = Get-Cluster -Name $Clustername).count -lt 1) { + $Validate = $False + thow "No Cluster '$myCluster' found!" + } + +} + +Process { + + $MyView = @() + if ($Validate -eq $True) { + + foreach ($myVMhost in ($myCluster | Get-VMHost)) { + + $esxcli2 = Get-ESXCLI -VMHost $myVMhost -V2 + $niclist = $esxcli2.network.nic.list.invoke() + + $nicdetails = @() + foreach ($nic in $niclist) { + + $args = $esxcli2.network.nic.get.createargs() + $args.nicname = $nic.name + $nicdetail = $esxcli2.network.nic.get.Invoke($args) + $nicdetails += $nicdetail + + } + ForEach ($nicdetail in $nicdetails){ + $NICReport = [PSCustomObject] @{ + Host = $myVMhost.Name + vmnic = $nicdetail.Name + LinkStatus = $nicdetail.LinkStatus + BusInfo = $nicdetail.driverinfo.BusInfo + Driver = $nicdetail.driverinfo.Driver + FirmwareVersion = $nicdetail.driverinfo.FirmwareVersion + DriverVersion = $nicdetail.driverinfo.Version + } + $MyView += $NICReport + } + + } + + $MyView + + } +} +} \ No newline at end of file diff --git a/Modules/Get-NewAndRemovedVMs.psm1 b/Modules/Get-NewAndRemovedVMs/Get-NewAndRemovedVMs.psm1 similarity index 100% rename from Modules/Get-NewAndRemovedVMs.psm1 rename to Modules/Get-NewAndRemovedVMs/Get-NewAndRemovedVMs.psm1 diff --git a/Modules/Get-VMmaxIOPS.psm1 b/Modules/Get-VMmaxIOPS/Get-VMmaxIOPS.psm1 similarity index 100% rename from Modules/Get-VMmaxIOPS.psm1 rename to Modules/Get-VMmaxIOPS/Get-VMmaxIOPS.psm1 diff --git a/Modules/InstantClone/InstantClone.psm1 b/Modules/InstantClone/InstantClone.psm1 new file mode 100644 index 0000000..a54865a --- /dev/null +++ b/Modules/InstantClone/InstantClone.psm1 @@ -0,0 +1,98 @@ +Function New-InstantClone { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: Apr 29, 2018 + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function demonstrates the use of the new "Parentless" Instant Clone + API that was introduced in vSphere 6.7 + .DESCRIPTION + Function to create new "Parentless" Instant Clones in vSphere 6.7 + .EXAMPLE + $SourceVM = "Foo" + $newVMName = Foo-IC-1 + $guestCustomizationValues = @{ + "guestinfo.ic.hostname" = $newVMName + "guestinfo.ic.ipaddress" = "192.168.30.10" + "guestinfo.ic.netmask" = "255.255.255.0" + "guestinfo.ic.gateway" = "192.168.30.1" + "guestinfo.ic.dns" = "192.168.30.1" + } + New-InstantClone -SourceVM $SourceVM -DestinationVM $newVMName -CustomizationFields $guestCustomizationValues + .NOTES + Make sure that you have both a vSphere 6.7 env (VC/ESXi) as well as + as the latest PowerCLI 10.1 installed which is reuqired to use vSphere 6.7 APIs +#> + param( + [Parameter(Mandatory=$true)][String]$SourceVM, + [Parameter(Mandatory=$true)][String]$DestinationVM, + [Parameter(Mandatory=$true)][Hashtable]$CustomizationFields + ) + $vm = Get-VM -Name $SourceVM + + $config = @() + $CustomizationFields.GetEnumerator() | Foreach-Object { + $optionValue = New-Object VMware.Vim.OptionValue + $optionValue.Key = $_.Key + $optionValue.Value = $_.Value + $config += $optionValue + } + + # SourceVM must either be running or running but in Frozen State + if($vm.PowerState -ne "poweredOn") { + Write-Host -ForegroundColor Red "Instant Cloning is only supported on a PoweredOn or Frozen VM" + break + } + + # SourceVM == Powered On + if((Get-VM $SourceVM).ExtensionData.Runtime.InstantCloneFrozen -eq $false) { + + # Retrieve all Network Adapters for SourceVM + $vmNetworkAdapters = @() + $devices = $vm.ExtensionData.Config.Hardware.Device + foreach ($device in $devices) { + if($device -is [VMware.Vim.VirtualEthernetCard]) { + $vmNetworkAdapters += $device + } + } + + $spec = New-Object VMware.Vim.VirtualMachineInstantCloneSpec + $locationSpec = New-Object VMware.Vim.VirtualMachineRelocateSpec + + # Disconect all NICs for new Instant Clone to ensure no dupe addresses on network + # post-Instant Clone workflow needs to renable after uypdating GuestOS + foreach ($vmNetworkAdapter in $vmNetworkAdapters) { + $networkName = $vmNetworkAdapter.backing.deviceName + $deviceConfigSpec = New-Object VMware.Vim.VirtualDeviceConfigSpec + $deviceConfigSpec.Operation = "edit" + $deviceConfigSpec.Device = $vmNetworkAdapter + $deviceConfigSpec.Device.backing = New-Object VMware.Vim.VirtualEthernetCardNetworkBackingInfo + $deviceConfigSpec.device.backing.deviceName = $networkName + $connectable = New-Object VMware.Vim.VirtualDeviceConnectInfo + $connectable.MigrateConnect = "disconnect" + $deviceConfigSpec.Device.Connectable = $connectable + $locationSpec.DeviceChange += $deviceConfigSpec + } + + $spec.Config = $config + $spec.Location = $locationSpec + $spec.Name = $DestinationVM + # SourceVM == Frozen + } else { + $spec = New-Object VMware.Vim.VirtualMachineInstantCloneSpec + $locationSpec = New-Object VMware.Vim.VirtualMachineRelocateSpec + $spec.Config = $config + $spec.Location = $locationSpec + $spec.Name = $DestinationVM + } + + Write-Host "Creating Instant Clone $DestinationVM ..." + $task = $vm.ExtensionData.InstantClone_Task($spec) + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task | Out-Null +} \ No newline at end of file diff --git a/Modules/Konfig-ESXi.psm1 b/Modules/Konfig-ESXi/Konfig-ESXi.psm1 similarity index 100% rename from Modules/Konfig-ESXi.psm1 rename to Modules/Konfig-ESXi/Konfig-ESXi.psm1 diff --git a/Modules/NSXT/NSXT.psd1 b/Modules/NSXT/NSXT.psd1 new file mode 100644 index 0000000..3cee460 --- /dev/null +++ b/Modules/NSXT/NSXT.psd1 @@ -0,0 +1,46 @@ +@{ + ModuleToProcess = 'NSXT.psm1' + ModuleVersion = '1.0.0.0' + GUID = 'c72f4e3d-5d1d-498f-ba86-6fa03e4ae6dd' + Author = 'William Lam' + CompanyName = 'primp-industries.com' + Copyright = '(c) 2017. All rights reserved.' + Description = 'Powershell Module for NSX-T REST API Functions' + PowerShellVersion = '5.0' + FunctionsToExport = 'Get-NSXTBGPNeighbors', + 'Get-NSXTComputeManager', + 'Get-NSXTController', + 'Get-NSXTEdgeCluster', + 'Get-NSXTFabricNode', + 'Get-NSXTFabricVM', + 'Get-NSXTFirewallRule', + 'Get-NSXTForwardingTable', + 'Get-NSXTIPPool', + 'Get-NSXTLogicalRouter', + 'Get-NSXTLogicalRouterPorts', + 'Get-NSXTLogicalSwitch', + 'Get-NSXTManager', + 'Get-NSXTNetworkRoutes', + 'Get-NSXTRoutingTable', + 'Get-NSXTTraceFlow', + 'Get-NSXTTraceFlowObservations', + 'Get-NSXTTransportNode', + 'Get-NSXTTransportZone', + 'Get-NSXTClusterNode', + 'Set-NSXTIPPool', + 'Set-NSXTLogicalRouter', + 'Set-NSXTLogicalSwitch', + 'Set-NSXTTraceFlow', + 'Get-NSXTIPAMIPBlock', + 'Set-NSXTIPAMIPBlock', + 'Remove-NSXTIPAMIPBlock' + + + PrivateData = @{ + PSData = @{ + Tags = @('NSX-T','REST') + LicenseUri = 'https://www.tldrlegal.com/l/mit' + ProjectUri = 'https://github.com/lamw/PowerCLI-Example-Scripts/tree/master/Modules/NSXT' + } + } +} \ No newline at end of file diff --git a/Modules/NSXT/NSXT.psm1 b/Modules/NSXT/NSXT.psm1 new file mode 100644 index 0000000..4e135e8 --- /dev/null +++ b/Modules/NSXT/NSXT.psm1 @@ -0,0 +1,2104 @@ +Function Get-NSXTController { + Param ( + [parameter(Mandatory=$false,ValueFromPipeline=$true)][string]$Id + ) + + $clusterNodeService = Get-NsxtService -Name "com.vmware.nsx.cluster.nodes" + $clusterNodeStatusService = Get-NsxtService -Name "com.vmware.nsx.cluster.nodes.status" + if($Id) { + $nodes = $clusterNodeService.get($Id) + } else { + $nodes = $clusterNodeService.list().results | where { $_.manager_role -eq $null } + } + + $results = @() + foreach ($node in $nodes) { + $nodeId = $node.id + $nodeName = $node.controller_role.control_plane_listen_addr.ip_address + $nodeStatusResults = $clusterNodeStatusService.get($nodeId) + + $tmp = [pscustomobject] @{ + Id = $nodeId; + Name = $nodeName; + ClusterStatus = $nodeStatusResults.control_cluster_status.control_cluster_status; + Version = $nodeStatusResults.version; + + } + $results+=$tmp + } + $results +} + +Function Get-NSXTFabricNode { + Param ( + [parameter(Mandatory=$false,ValueFromPipeline=$true)][string]$Id, + [Switch]$ESXi, + [Switch]$Edge + ) + + $fabricNodeService = Get-NsxtService -Name "com.vmware.nsx.fabric.nodes" + $fabricNodeStatusService = Get-NsxtService -Name "com.vmware.nsx.fabric.nodes.status" + if($Id) { + $nodes = $fabricNodeService.get($Id) + } else { + if($ESXi) { + $nodes = $fabricNodeService.list().results | where { $_.resource_type -eq "HostNode" } + } elseif ($Edge) { + $nodes = $fabricNodeService.list().results | where { $_.resource_type -eq "EdgeNode" } + } else { + $nodes = $fabricNodeService.list().results + } + } + + $results = @() + foreach ($node in $nodes) { + $nodeStatusResult = $fabricNodeStatusService.get($node.id) + + $tmp = [pscustomobject] @{ + Id = $node.id; + Name = $node.display_name; + Type = $node.resource_type; + Address = $node.ip_addresses; + NSXVersion = $nodeStatusResult.software_version + OS = $node.os_type; + Version = $node.os_version; + Status = $nodeStatusResult.host_node_deployment_status + ManagerStatus = $nodeStatusResult.mpa_connectivity_status + ControllerStatus = $nodeStatusResult.lcp_connectivity_status + } + $results+=$tmp + } + $results +} + +Function Get-NSXTComputeManager { + Param ( + [parameter(Mandatory=$false,ValueFromPipeline=$true)][string]$Id + ) + + $computeManagerSerivce = Get-NsxtService -Name "com.vmware.nsx.fabric.compute_managers" + $computeManagerStatusService = Get-NsxtService -Name "com.vmware.nsx.fabric.compute_managers.status" + + if($Id) { + $computeManagers = $computeManagerSerivce.get($id) + } else { + $computeManagers = $computeManagerSerivce.list().results + } + + $results = @() + foreach ($computeManager in $computeManagers) { + $computeManagerStatus = $computeManagerStatusService.get($computeManager.Id) + + $tmp = [pscustomobject] @{ + Id = $computeManager.Id; + Name = $computeManager.display_name; + Server = $computeManager.server + Type = $computeManager.origin_type; + Version = $computeManagerStatus.Version; + Registration = $computeManagerStatus.registration_status; + Connection = $computeManagerStatus.connection_status; + } + $results+=$tmp + } + $results +} + +Function Get-NSXTFirewallRule { + Param ( + [parameter(Mandatory=$false,ValueFromPipeline=$true)][string]$Id + ) + + $firewallService = Get-NsxtService -Name "com.vmware.nsx.firewall.sections" + $firewallRuleService = Get-NsxtService -Name "com.vmware.nsx.firewall.sections.rules" + + if($Id) { + $firewallRuleSections = $firewallService.get($Id) + } else { + $firewallRuleSections = $firewallService.list().results + } + + $sectionResults = @() + foreach ($firewallRuleSection in $firewallRuleSections) { + $tmp = [pscustomobject] @{ + Id = $firewallRuleSection.Id; + Name = $firewallRuleSection.display_name; + Type = $firewallRuleSection.section_type; + Stateful = $firewallRuleSection.stateful; + RuleCount = $firewallRuleSection.rule_count; + } + $sectionResults+=$tmp + } + $sectionResults + + $firewallResults = @() + if($id) { + $firewallRules = $firewallRuleService.list($id).results + foreach ($firewallRule in $firewallRules) { + $tmp = [pscustomobject] @{ + Id = $firewallRule.id; + Name = $firewallRule.display_name; + Sources = if($firewallRule.sources -eq $null) { "ANY" } else { $firewallRule.sources}; + Destination = if($firewallRule.destinations -eq $null) { "ANY" } else { $firewallRule.destinations }; + Services = if($firewallRule.services -eq $null) { "ANY" } else { $firewallRule.services } ; + Action = $firewallRule.action; + AppliedTo = if($firewallRule.applied_tos -eq $null) { "ANY" } else { $firewallRule.applied_tos }; + Log = $firewallRule.logged; + } + $firewallResults+=$tmp + } + } + $firewallResults +} + +Function Get-NSXTManager { + $clusterNodeService = Get-NsxtService -Name "com.vmware.nsx.cluster.nodes" + + $nodes = $clusterNodeService.list().results + + $results = @() + foreach ($node in $nodes) { + if($node.manager_role -ne $null) { + $tmp = [pscustomobject] @{ + Id = $node.id; + Name = $node.display_name; + Address = $node.appliance_mgmt_listen_addr; + SHA256Thumbprint = $node.manager_role.api_listen_addr.certificate_sha256_thumbprint; + } + $results+=$tmp + } + } + $results +} + +# Updated Function style below + +Function Get-NSXTTransportNode { + <# + .Synopsis + Retrieves the transport_node information + .DESCRIPTION + Retrieves transport_node information for a single or multiple IDs. Execute with no parameters to get all ports, specify a transport_node if known. + .EXAMPLE + Get-NSXTTransportNode + .EXAMPLE + Get-NSXTThingTemplate -Tranport_node_id "TN ID" +#> + + Param ( + [parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] + [Alias("Id","Tranportnode_id")] + [string]$transport_node_id + ) + + begin + { + $NSXTransportNodesService = Get-NsxtService -Name "com.vmware.nsx.transport_nodes" + + class NSXTransportNode { + [string]$Name + [string]$Transport_node_id + [string]$maintenance_mode + hidden $tags = [System.Collections.Generic.List[string]]::new() + hidden $host_switches = [System.Collections.Generic.List[string]]::new() + hidden [string]$host_switch_spec + hidden $transport_zone_endpoints = [System.Collections.Generic.List[string]]::new() + } + } + + Process + { + if($transport_node_id) { + $NSXTransportNodes = $NSXTransportNodesService.get($transport_node_id) + } else { + $NSXTransportNodes = $NSXTransportNodesService.list().results + } + + foreach ($NSXTransportNode in $NSXTransportNodes) { + + $results = [NSXTransportNode]::new() + $results.Name = $NSXTransportNode.display_name; + $results.Transport_node_id = $NSXTransportNode.Id; + $results.maintenance_mode = $NSXTransportNode.maintenance_mode; + $results.Tags = $NSXTransportNode.tags; + $results.host_switches = $NSXTransportNode.host_switches; + $results.host_switch_spec = $NSXTransportNode.host_switch_spec; + $results.transport_zone_endpoints = $NSXTransportNode.transport_zone_endpoints; + $results.host_switches = $NSXTransportNode.host_switches + $results + } + } +} + +Function Get-NSXTTraceFlow { + <# + .Synopsis + Retrieves traceflow information + .DESCRIPTION + Retrieves traceflow information for a single or multiple traceflows. Execute with no parameters to get all traceflows, specify a traceflow_id if known. + .EXAMPLE + Get-NSXTTraceFlow + .EXAMPLE + Get-NSXTTraceFlow -traceflow_id "TF ID +#> + + Param ( + [parameter(Mandatory=$false,ValueFromPipeline=$true)] + [Alias("Id")] + [string]$traceflow_id + ) + + $NSXTraceFlowsService = Get-NsxtService -Name "com.vmware.nsx.traceflows" + + if($traceflow_id) { + $NSXTraceFlows = $NSXTraceFlowsService.get($traceflow_id) + } else { + $NSXTraceFlows = $NSXTraceFlowsService.list().results + } + + class NSXTraceFlow { + [string]$traceflow_id + hidden [string]$lport_id + [string]$Operation_State + [int]$Forwarded + [int]$Delivered + [int]$Received + [int]$Dropped + [string]$Analysis + } + + foreach ($NSXTraceFlow in $NSXTraceFlows) { + + $results = [NSXTraceFlow]::new() + $results.traceflow_id = $NSXTraceFlow.Id; + $results.Operation_State = $NSXTraceFlow.operation_state; + $results.forwarded = $NSXTraceFlow.Counters.forwarded_count; + $results.delivered = $NSXTraceFlow.Counters.delivered_count; + $results.received = $NSXTraceFlow.Counters.received_count; + $results.dropped = $NSXTraceFlow.Counters.dropped_count; + $results.analysis = $NSXTraceFlow.analysis + $results + } +} + +Function Get-NSXTTraceFlowObservations { + <# + .Synopsis + Retrieves traceflow observations information + .DESCRIPTION + Retrieves traceflow observations information for a single traceflow. Must specify a current traceflow_id + .EXAMPLE + Get-NSXTTraceFlowObservations -traceflow_id "TF ID" + .EXAMPLE + Get-NSXTTraceFlow | Get-NSXTTraceFlowObservations +#> + + Param ( + [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] + [Alias("Id")] + [string]$traceflow_id + ) + + begin + { + $NSXTraceFlowsObservService = Get-NsxtService -Name "com.vmware.nsx.traceflows.observations" + } + + Process + { + if($traceflow_id) { + $NSXTraceFlowsObserv = $NSXTraceFlowsObservService.list($traceflow_id) + } else { + throw "TraceFlow ID required" + } + + $NSXTraceFlowsObserv.results | select transport_node_name,component_name,@{N='PacketEvent';E={($_.resource_type).TrimStart("TraceflowObservation")}} + } +} + +Function Get-NSXTEdgeCluster { + <# + .Synopsis + Retrieves the Edge cluster information + .DESCRIPTION + Retrieves Edge cluster information for a single or multiple clusterss. Execute with no parameters to get all ports, specify a edge_cluster_id if known. + .EXAMPLE + Get-NSXTEdgeCluster + .EXAMPLE + Get-NSXTEdgeCluster -edge_cluster_id "Edge Cluster ID" + .EXAMPLE + Get-NSXTThingTemplate | where name -eq "My Edge Cluster Name" +#> + + Param ( + [parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] + [Alias("Id")] + [string]$edge_cluster_id + ) + + Begin + { + $NSXTEdgeClustersService = Get-NsxtService -Name "com.vmware.nsx.edge_clusters" + + class NSXEdgeCluster { + [string]$Name + hidden [string]$Protection + hidden [string]$Tags + [string]$edge_cluster_id + [string]$resource_type + [string]$deployment_type + [string]$member_node_type + $members = [System.Collections.Generic.List[string]]::new() + $cluster_profile_bindings = [System.Collections.Generic.List[string]]::new() + } + } + + Process + { + if ($edge_cluster_id) { + $NSXEdgeClusters = $NSXTEdgeClustersService.get($edge_cluster_id) + } + else { + $NSXEdgeClusters = $NSXTEdgeClustersService.list().results + } + + foreach ($NSXEdgeCluster in $NSXEdgeClusters) { + + $results = [NSXEdgeCluster]::new() + $results.Name = $NSXEdgeCluster.display_name; + $results.Protection = $NSXEdgeCluster.Protection; + $results.edge_cluster_id = $NSXEdgeCluster.Id; + $results.resource_type = $NSXEdgeCluster.resource_type; + $results.Tags = $NSXEdgeCluster.tags; + $results.deployment_type = $NSXEdgeCluster.deployment_type; + $results.member_node_type = $NSXEdgeCluster.member_node_type; + $results.members = $NSXEdgeCluster.members; + $results.cluster_profile_bindings = $NSXEdgeCluster.cluster_profile_bindings + $results + } + } +} + +Function Get-NSXTLogicalRouter { + <# + .Synopsis + Retrieves the Logical Router information + .DESCRIPTION + Retrieves Logical Router information for a single or multiple LR's. This includes corresponding SR's and transport_node_id. Execute with no parameters to get all ports, specify a Logical_router_id if known. + .EXAMPLE + Get-NSXTLogicalRouter + .EXAMPLE + Get-NSXTLogicalRouter -Logical_router_id "LR ID" + .EXAMPLE + Get-NSXTLogicalRouter | where name -eq "LR Name" + .EXAMPLE + (Get-NSXTLogicalRouter -Logical_router_id "LR ID").per_node_status +#> + + Param ( + [parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] + [Alias("Id")] + [string]$Logical_router_id + ) + + begin + { + $NSXTLogicalRoutersService = Get-NsxtService -Name "com.vmware.nsx.logical_routers" + $NSXTLogicalRoutersStatusService = Get-NsxtService -Name "com.vmware.nsx.logical_routers.status" + + class per_node_status { + $service_router_id + [ValidateSet("ACTIVE","STANDBY","DOWN","SYNC","UNKNOWN")] + $high_availability_status + $transport_node_id + + per_node_status(){} + + per_node_status( + $service_router_id, + $high_availability_status, + $transport_node_id + ) { + $this.service_router_id = $service_router_id + $this.high_availability_status = $high_availability_status + $this.transport_node_id = $transport_node_id + } + } + + class NSXTLogicalRouter { + [string]$Name + [string]$Logical_router_id + [string]$protection + hidden [string]$Tags + [string]$edge_cluster_id + [ValidateSet("TIER0","TIER1")] + [string]$router_type + [ValidateSet("ACTIVE_ACTIVE","ACTIVE_STANDBY","")] + [string]$high_availability_mode + [ValidateSet("PREEMPTIVE","NON_PREEMPTIVE","")] + [string]$failover_mode + [string]$external_transit + [string]$internal_transit + hidden [string]$advanced_config = [System.Collections.Generic.List[string]]::new() + hidden [string]$firewall_sections = [System.Collections.Generic.List[string]]::new() + $per_node_status = [System.Collections.Generic.List[string]]::new() + } + } + + Process + { + if($Logical_router_id) { + $NSXLogicalRouters = $NSXTLogicalRoutersService.get($Logical_router_id) + } else { + $NSXLogicalRouters = $NSXTLogicalRoutersService.list().results + } + + foreach ($NSXLogicalRouter in $NSXLogicalRouters) { + + $NSXTLogicalRoutersStatus = $NSXTLogicalRoutersStatusService.get($NSXLogicalRouter.id) + $results = [NSXTLogicalRouter]::new() + + foreach ($NSXTLogicalRouterStatus in $NSXTLogicalRoutersStatus.per_node_status) { + $results.per_node_status += [per_node_status]::new($NSXTLogicalRouterStatus.service_router_id,$NSXTLogicalRouterStatus.high_availability_status,$NSXTLogicalRouterStatus.transport_node_id) + } + + $results.Name = $NSXLogicalRouter.display_name; + $results.Logical_router_id = $NSXLogicalRouter.Id; + $results.protection = $NSXLogicalRouter.protection; + $results.Tags = $NSXLogicalRouter.tags; + $results.edge_cluster_id = $NSXLogicalRouter.edge_cluster_id; + $results.router_type = $NSXLogicalRouter.router_type; + $results.high_availability_mode = $NSXLogicalRouter.high_availability_mode; + $results.failover_mode =$NSXLogicalRouter.failover_mode; + $results.external_transit = $NSXLogicalRouter.advanced_config.external_transit_networks; + $results.internal_transit = $NSXLogicalRouter.advanced_config.internal_transit_network; + $results.advanced_config =$NSXLogicalRouter.advanced_config; + $results.firewall_sections =$NSXLogicalRouter.firewall_sections + $results + } + } +} + +Function Get-NSXTRoutingTable { + <# + .Synopsis + Retrieves the routing table information + .DESCRIPTION + Retrieves routing table for a single LR including LR type (SR/DR) and next_hop. Must specify Logical_router_id & transport_node_id. Pipeline input supported. + .EXAMPLE + Get-NSXTRoutingTable -Logical_router_id "LR ID" -transport_node_id "TN ID" | format-table -autosize + .EXAMPLE + Get-NSXTLogicalRouter | where name -eq "LR Name" | Get-NSXTRoutingTable -transport_node_id "TN ID" + .EXAMPLE + Get-NSXTLogicalRouter | where name -eq INT-T1 | Get-NSXTRoutingTable -transport_node_id ((Get-NSXTTransportNode | where name -match "INT")[0].transport_node_id) + .EXAMPLE + Get-NSXTLogicalRouter | where name -eq INT-T1 | Get-NSXTRoutingTable -transport_node_id (((Get-NSXTLogicalRouter | where name -eq INT-T1).per_node_status | where high_availability_status -eq ACTIVE).transport_node_id) +#> + + Param ( + [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] + [string]$Logical_router_id, + [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] + [string]$transport_node_id + ) + + Begin + { + $NSXTRoutingTableService = Get-NsxtService -Name "com.vmware.nsx.logical_routers.routing.route_table" + + class NSXTRoutingTable { + hidden [string]$Logical_router_id + [string]$lr_component_id + [string]$lr_component_type + [string]$network + [string]$next_hop + [string]$route_type + hidden [string]$logical_router_port_id + [long]$admin_distance + } + } + + Process + { + $NSXTRoutingTable = $NSXTRoutingTableService.list($Logical_router_id,$transport_node_id,$null,$null,$null,$null,$null,'realtime') + + foreach ($NSXTRoute in $NSXTRoutingTable.results) { + + $results = [NSXTRoutingTable]::new() + $results.Logical_router_id = $Logical_router_id; + $results.lr_component_type = $NSXTRoute.lr_component_type; + $results.lr_component_id = $NSXTRoute.lr_component_id; + $results.next_hop = $NSXTRoute.next_hop; + $results.route_type = $NSXTRoute.route_type; + $results.logical_router_port_id = $NSXTRoute.logical_router_port_id; + $results.admin_distance = $NSXTRoute.admin_distance; + $results.network = $NSXTRoute.network + $results + } + } +} + +Function Get-NSXTFabricVM { + <# + .Synopsis + Retrieves the VM's attached to the fabric. + .DESCRIPTION + Retrieves all VM's attached to the fabric. + .EXAMPLE + Get-NSXTFabricVM +#> + Begin + { + $NSXTVMService = Get-NsxtService -Name "com.vmware.nsx.fabric.virtual_machines" + + class NSXVM { + [string]$Name + $resource_type + hidden [string]$Tags + hidden $compute_ids + hidden [string]$external_id + [string]$host_id + [string]$power_state + [string]$type + hidden $source + } + } + + Process + { + + $NSXTVMs = $NSXTVMService.list().results + + foreach ($NSXTVM in $NSXTVMs) { + + $results = [NSXVM]::new() + $results.Name = $NSXTVM.display_name; + $results.resource_type = $NSXTVM.resource_type; + $results.compute_ids = $NSXTVM.compute_ids; + $results.resource_type = $NSXTVM.resource_type; + $results.Tags = $NSXTVM.tags; + $results.external_id = $NSXTVM.external_id; + $results.host_id = $NSXTVM.host_id; + $results.power_state = $NSXTVM.power_state; + $results.type = $NSXTVM.type; + $results.source = $NSXTVM.source + $results + } + } +} + +Function Get-NSXTBGPNeighbors { + <# + .Synopsis + Retrieves the BGP neighbor information + .DESCRIPTION + Retrieves BGP neighbor information for a single logical router. Must specify logical_router_id parameter. Pipeline input supported + .EXAMPLE + Get-NSXTBGPNeighbors -logical_router_id "LR ID" + .EXAMPLE + Get-NSXTLogicalRouter | where name -eq "LR Name" | Get-NSXTBGPNeighbors +#> + + Param ( + [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] + [Alias("Id")] + [string]$logical_router_id + ) + + begin + { + $NSXTThingsService = Get-NsxtService -Name "com.vmware.nsx.logical_routers.routing.bgp.neighbors" + + class NSXTBGPNeighbors { + [string]$Name + [string]$logical_router_id + hidden $tags = [System.Collections.Generic.List[string]]::new() + [string]$protection + [string]$resource_type + [string]$address_families = [System.Collections.Generic.List[string]]::new() + hidden $bfd_config + [bool]$enable_bfd + [bool]$enabled + hidden $filter_in_ipprefixlist_id + hidden $filter_in_routemap_id + hidden $filter_out_ipprefixlist_id + hidden $filter_out_routemap_id + hidden [long]$hold_down_timer + hidden [long]$keep_alive_timer + hidden [long]$maximum_hop_limit + [string]$neighbor_address + hidden [string]$password + [long]$remote_as + [string]$remote_as_num + [string]$source_address + [string]$source_addresses = [System.Collections.Generic.List[string]]::new() + } + } + + Process + { + $NSXTThings = $NSXTThingsService.list($logical_router_id).results + + foreach ($NSXTThing in $NSXTThings) { + + $results = [NSXTBGPNeighbors]::new() + $results.Name = $NSXTThing.display_name; + $results.logical_router_id = $NSXTThing.logical_router_id; + $results.tags = $NSXTThing.tags; + $results.protection = $NSXTThing.protection; + $results.resource_type = $NSXTThing.resource_type; + $results.address_families = $NSXTThing.address_families; + $results.bfd_config = $NSXTThing.bfd_config; + $results.enable_bfd = $NSXTThing.enable_bfd; + $results.enabled = $NSXTThing.enabled; + $results.filter_in_ipprefixlist_id = $NSXTThing.filter_in_ipprefixlist_id; + $results.filter_in_routemap_id = $NSXTThing.filter_in_routemap_id; + $results.filter_out_ipprefixlist_id = $NSXTThing.filter_out_ipprefixlist_id; + $results.filter_out_routemap_id = $NSXTThing.filter_out_routemap_id; + $results.hold_down_timer = $NSXTThing.hold_down_timer; + $results.keep_alive_timer = $NSXTThing.keep_alive_timer; + $results.maximum_hop_limit = $NSXTThing.maximum_hop_limit; + $results.neighbor_address = $NSXTThing.neighbor_address; + $results.password = $NSXTThing.password; + $results.remote_as = $NSXTThing.remote_as; + $results.remote_as_num = $NSXTThing.remote_as_num; + $results.source_address = $NSXTThing.source_address; + $results.source_addresses = $NSXTThing.source_addresses + $results + } + } +} + +Function Get-NSXTForwardingTable { + <# + .Synopsis + Retrieves the forwarding table information + .DESCRIPTION + Retrieves forwarding table for a single LR including LR type (SR/DR) and next_hop. Must specify Logical_router_id & transport_node_id. Pipeline input supported. + .EXAMPLE + Get-Get-NSXTForwardingTable -Logical_router_id "LR ID" -transport_node_id "TN ID" | format-table -autosize + .EXAMPLE + Get-NSXTLogicalRouter | where name -eq "LR Name" | Get-NSXTForwardingTable -transport_node_id "TN ID" + .EXAMPLE + Get-NSXTLogicalRouter | where name -eq "LR Name" | Get-NSXTForwardingTable -transport_node_id ((Get-NSXTTransportNode | where name -match "Edge Name")[0].transport_node_id) + .EXAMPLE + Get-NSXTLogicalRouter | where name -eq "LR Name" | Get-NSXTForwardingTable -transport_node_id (((Get-NSXTLogicalRouter | where name -eq "Edge Name").per_node_status | where high_availability_status -eq ACTIVE).transport_node_id) +#> + + Param ( + [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] + [string]$Logical_router_id, + [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] + [string]$transport_node_id + ) + + Begin + { + $NSXTForwardingTableService = Get-NsxtService -Name "com.vmware.nsx.logical_routers.routing.forwarding_table" + + class NSXTForwardingTable { + hidden [string]$Logical_router_id + [string]$lr_component_id + [string]$lr_component_type + [string]$network + [string]$next_hop + [string]$route_type + hidden [string]$logical_router_port_id + } + } + + Process + { + $NSXTForwardingTable = $NSXTForwardingTableService.list($Logical_router_id,$transport_node_id,$null,$null,$null,$null,$null,$null,'realtime') + + foreach ($NSXTForwarding in $NSXTForwardingTable.results) { + + $results = [NSXTForwardingTable]::new() + $results.Logical_router_id = $Logical_router_id; + $results.lr_component_type = $NSXTForwarding.lr_component_type; + $results.lr_component_id = $NSXTForwarding.lr_component_id; + $results.network = $NSXTForwarding.network; + $results.next_hop = $NSXTForwarding.next_hop; + $results.route_type = $NSXTForwarding.route_type; + $results.logical_router_port_id = $NSXTForwarding.logical_router_port_id + $results + } + } +} + +Function Get-NSXTNetworkRoutes { +<# + .Synopsis + Retrieves the network routes information + .DESCRIPTION + Retrieves the network routes information for a single or multiple routes. + .EXAMPLE + Get-NSXTNetworkRoutes + .EXAMPLE + Get-NSXTNetworkRoutes -route_id "Route ID" +#> + + Param ( + [parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] + [string]$route_id + ) + + Begin + { + $NSXTNetworkRoutesService = Get-NsxtService -Name "com.vmware.nsx.node.network.routes" + + class NSXTNetworkRoutes { + [string]$route_id + $route_type + $interface_id + $gateway + $from_address + $destination + $netmask + $metric + $proto + $scope + $src + } + } + + Process + { + if ($route_id) { + $NSXTNetworkRoutes = $NSXTNetworkRoutesService.get($route_id) + } + else { + $NSXTNetworkRoutes = $NSXTNetworkRoutesService.list().results + } + + foreach ($NSXTRoute in $NSXTNetworkRoutes) { + + $results = [NSXTNetworkRoutes]::new() + $results.route_id = $NSXTRoute.route_id; + $results.route_type = $NSXTRoute.route_type; + $results.interface_id = $NSXTRoute.interface_id; + $results.gateway = $NSXTRoute.gateway; + $results.from_address = $NSXTRoute.from_address; + $results.destination = $NSXTRoute.destination; + $results.netmask = $NSXTRoute.netmask; + $results.metric = $NSXTRoute.metric; + $results.proto = $NSXTRoute.proto; + $results.scope = $NSXTRoute.scope; + $results.src = $NSXTRoute.src + $results + } + } +} + +Function Get-NSXTLogicalRouterPorts { +<# + .Synopsis + Retrieves the logical router port information + .DESCRIPTION + Retrieves logical router port information for a single or multiple ports. Execute with no parameters to get all ports, specify a single port if known, or feed a logical switch for filtered output. + .EXAMPLE + Get-NSXTLogicalRouterPorts + .EXAMPLE + Get-NSXTLogicalRouterPorts -logical_router_port_id "LR Port Name" + .EXAMPLE + Get-NSXTLogicalRouterPorts -logical_router_id "LR Name" + .EXAMPLE + Get-NSXTLogicalRouterPorts -logical_router_id (Get-NSXTLogicalRouter | where name -eq "LR Name") +#> + + Param ( + [parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] + [Alias("Id")] + [string]$logical_router_port_id, + [parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] + [string]$logical_router_id + ) + + begin + { + $NSXTLogicalRouterPortsService = Get-NsxtService -Name "com.vmware.nsx.logical_router_ports" + + class subnets { + $ip_addresses + $prefix_length + + subnets(){} + + subnets( + $ip_addresses, + $prefix_length + ) { + $this.ip_addresses = $ip_addresses + $this.prefix_length = $prefix_length + } + } + + class NSXTLogicalRouterPorts { + [string]$Name + $Id + [string]$logical_router_id + $resource_type + [string]$protection + $mac_address + $subnets = [System.Collections.Generic.List[string]]::new() + hidden [string]$Tags + hidden $linked_logical_switch_port_id + } + } + + Process + { + if($logical_router_port_id) { + $NSXTLogicalRouterPorts = $NSXTLogicalRouterPortsService.get($logical_router_port_id) + } else { + if ($logical_router_id) { + $NSXTLogicalRouterPorts = $NSXTLogicalRouterPortsService.list().results | where {$_.logical_router_id -eq $Logical_router_id} + } + else { + $NSXTLogicalRouterPorts = $NSXTLogicalRouterPortsService.list().results + } + } + + foreach ($NSXTLogicalRouterPort in $NSXTLogicalRouterPorts) { + + $results = [NSXTLogicalRouterPorts]::new() + + foreach ($subnet in $NSXTLogicalRouterPort.subnets) { + $results.subnets += [subnets]::new($subnet.ip_addresses,$subnet.prefix_length) + } + + $results.Name = $NSXTLogicalRouterPort.display_name + $results.Id = $NSXTLogicalRouterPort.Id + $results.Logical_router_id = $NSXTLogicalRouterPort.Logical_router_id + $results.resource_type = $NSXTLogicalRouterPort.resource_type + $results.protection = $NSXTLogicalRouterPort.protection + $results.Tags = $NSXTLogicalRouterPort.tags + $results.mac_address = $NSXTLogicalRouterPort.mac_address + $results.linked_logical_switch_port_id = $NSXTLogicalRouterPort.linked_logical_switch_port_id + $results + } + } +} + +Function Get-NSXTTransportZone { + <# + .Synopsis + Retrieves the Transport Zone information + .DESCRIPTION + Retrieves THING information for a single or multiple ports. Execute with no parameters to get all ports, specify a PARAM if known. + .EXAMPLE + Get-NSXTTransportZone + .EXAMPLE + Get-NSXTTransportZone -zone_id "Zone ID" + .EXAMPLE + Get-NSXTTransportZone -name "Zone1" +#> + + Param ( + [parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] + [Alias("Id")] + [string]$zone_id, + [parameter(Mandatory=$false)] + [string]$name + ) + + begin + { + $NSXTTransportZoneService = Get-NsxtService -Name "com.vmware.nsx.transport_zones" + + class NSXTTransportZone { + [string]$Name + [string]$ID + hidden [string]$description + hidden $tags + $resource_type + $host_switch_name + $transport_type + hidden $transport_zone_profile_ids + $host_switch_mode + $protection + hidden $uplink_teaming_policy_names + } + } + + Process + { + if($zone_id) { + $NSXTTransportZones = $NSXTTransportZoneService.get($zone_id) + } else { + if ($name) { + $NSXTTransportZones = $NSXTTransportZoneService.list().results | where {$_.display_name -eq $name} + } + else { + $NSXTTransportZones = $NSXTTransportZoneService.list().results + } + } + + foreach ($NSXTTransportZone in $NSXTTransportZones) { + + $results = [NSXTTransportZone]::new() + $results.Name = $NSXTTransportZone.display_name; + $results.ID = $NSXTTransportZone.Id; + $results.description = $NSXTTransportZone.description; + $results.tags = $NSXTTransportZone.tags; + $results.resource_type = $NSXTTransportZone.resource_type; + $results.host_switch_name = $NSXTTransportZone.host_switch_name; + $results.transport_type = $NSXTTransportZone.transport_type; + $results.transport_zone_profile_ids = $NSXTTransportZone.transport_zone_profile_ids; + $results.host_switch_mode = $NSXTTransportZone.host_switch_mode; + $results.protection = $NSXTTransportZone.protection; + $results.uplink_teaming_policy_names = $NSXTTransportZone.uplink_teaming_policy_names + $results + } + } +} + +Function Get-NSXTLogicalSwitch { + <# + .Synopsis + Retrieves the Logical Switch information + .DESCRIPTION + Retrieves Logical Switch information for a single or multiple switches. Execute with no parameters to get all ports, specify a name or lswitch_id if known. + .EXAMPLE + Get-NSXTLogicalSwitch + .EXAMPLE + Get-NSXTLogicalSwitch -lswitch_id "switch id" + .EXAMPLE + Get-NSXTLogicalSwitch -name "switch name" +#> + + Param ( + [parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] + [Alias("Id")] + [string]$lswitch_id, + [parameter(Mandatory=$false)] + [string]$name + ) + + begin + { + $NSXTLogicalSwitchService = Get-NsxtService -Name "com.vmware.nsx.logical_switches" + + class NSXTLogicalSwitch { + [string]$Name + [string]$ID + $tags + $resource_type + hidden $description + $vni + $transport_zone_id + $admin_state + $replication_mode + hidden $address_bindings + $protection + hidden $extra_configs + $ip_pool_id + hidden $mac_pool_id + hidden $uplink_teaming_policy_name + hidden $vlan + hidden $vlan_trunk_spec + } + } + + Process + { + if($lswitch_id) { + $NSXTLogicalSwitches = $NSXTLogicalSwitchService.get($lswitch_id) + } else { + if ($name) { + $NSXTLogicalSwitches = $NSXTLogicalSwitchService.list().results | where {$_.display_name -eq $name} + } + else { + $NSXTLogicalSwitches = $NSXTLogicalSwitchService.list().results + } + } + + foreach ($NSXTLogicalSwitch in $NSXTLogicalSwitches) { + + $results = [NSXTLogicalSwitch]::new() + $results.Name = $NSXTLogicalSwitch.display_name; + $results.Id = $NSXTLogicalSwitch.Id; + $results.Tags = $NSXTLogicalSwitch.tags; + $results.resource_type = $NSXTLogicalSwitch.resource_type; + $results.description = $NSXTLogicalSwitch.description; + $results.vni = $NSXTLogicalSwitch.vni; + $results.transport_zone_id = $NSXTLogicalSwitch.transport_zone_id; + $results.admin_state = $NSXTLogicalSwitch.admin_state; + $results.replication_mode = $NSXTLogicalSwitch.replication_mode; + $results.address_bindings = $NSXTLogicalSwitch.address_bindings; + $results.protection = $NSXTLogicalSwitch.protection; + $results.extra_configs = $NSXTLogicalSwitch.extra_configs; + $results.ip_pool_id = $NSXTLogicalSwitch.ip_pool_id; + $results.mac_pool_id = $NSXTLogicalSwitch.mac_pool_id; + $results.uplink_teaming_policy_name = $NSXTLogicalSwitch.uplink_teaming_policy_name; + $results.vlan = $NSXTLogicalSwitch.vlan; + $results.vlan_trunk_spec = $NSXTLogicalSwitch.vlan_trunk_spec + $results + } + } +} + +Function Get-NSXTIPPool { + <# + .Synopsis + Retrieves the THING information + .DESCRIPTION + Retrieves THING information for a single or multiple ports. Execute with no parameters to get all ports, specify a PARAM if known. + .EXAMPLE + Get-NSXTIPPool + .EXAMPLE + Get-NSXTThingTemplate -pool_id "Pool ID" +#> + + Param ( + [parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] + [Alias("Id")] + [string]$pool_id, + [parameter(Mandatory=$false)] + [string]$name + ) + + begin + { + $NSXTIPPoolService = Get-NsxtService -Name "com.vmware.nsx.pools.ip_pools" + + class NSXTIPPool { + [string]$Name + [string]$id + $total_ids + $free_ids + $allocated_ids + $Network + $Gateway + $DNS + $RangeStart + $RangeEnd + } + } + + Process + { + if($pool_id) { + $NSXTIPPools = $NSXTIPPoolService.get($pool_id) + } else { + if ($name) { + $NSXTIPPools = $NSXTIPPoolService.list().results | where {$_.display_name -eq $name} + } + else { + $NSXTIPPools = $NSXTIPPoolService.list().results + } + } + + foreach ($NSXTIPPool in $NSXTIPPools) { + + $results = [NSXTIPPool]::new() + $results.Name = $NSXTIPPool.display_name; + $results.ID = $NSXTIPPool.id; + $results.total_ids = $NSXTIPPool.pool_usage.total_ids; + $results.free_ids = $NSXTIPPool.pool_usage.free_ids; + $results.allocated_ids = $NSXTIPPool.pool_usage.allocated_ids; + $results.Network = $NSXTIPPool.subnets.cidr; + $results.Gateway = $NSXTIPPool.subnets.gateway_ip; + $results.DNS = $NSXTIPPool.subnets.dns_nameservers; + $results.RangeStart = $NSXTIPPool.subnets.allocation_ranges.start; + $results.RangeEnd = $NSXTIPPool.subnets.allocation_ranges.end + $results + } + } +} + +Function Get-NSXTIPAMIPBlock { + <# + .Synopsis + Retrieves the IPAM IP Block information + .DESCRIPTION + Retrieves IPAM IP Block information for a single or multiple ports. Execute with no parameters to get all ports, specify a PARAM if known. + .EXAMPLE + Get-NSXTIPAMIPBlock + .EXAMPLE + Get-NSXTIPAMIPBlock -block_id "Block Id" + .EXAMPLE + Get-NSXTIPAMIPBlock -name "Block Name" + +#> + + Param ( + [parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] + [Alias("Id")] + [string]$block_id, + [parameter(Mandatory=$false)] + [string]$name + ) + + begin + { + $NSXTIPAMIPBlocksService = Get-NsxtService -Name "com.vmware.nsx.pools.ip_blocks" + + class ip_block { + [string]$Name + [string]$block_id + hidden [string]$Tags = [System.Collections.Generic.List[string]]::new() + [string]$protection + #[ValidateSet("TIER0","TIER1")] + [string]$cidr + hidden [string]$resource_type + } + } + + Process + { + if($block_id) { + $NSXTIPAMIPBlocks = $NSXTIPAMIPBlocksService.get($block_id) + } else { + if ($name) { + $NSXTIPAMIPBlocks = $NSXTIPAMIPBlocksService.list().results | where {$_.display_name -eq $name} + } + else { + $NSXTIPAMIPBlocks = $NSXTIPAMIPBlocksService.list().results + } + } + + foreach ($NSXTIPAMIPBlock in $NSXTIPAMIPBlocks) { + + $results = [ip_block]::new() + $results.Name = $NSXTIPAMIPBlock.display_name; + $results.block_id = $NSXTIPAMIPBlock.id; + $results.Tags = $NSXTIPAMIPBlock.tags; + $results.protection = $NSXTIPAMIPBlock.protection; + $results.cidr = $NSXTIPAMIPBlock.cidr; + $results.resource_type = $NSXTIPAMIPBlock.resource_type + + $results + } + } +} + +Function Get-NSXTClusterNode { + <# + .Synopsis + Retrieves the cluster node information + .DESCRIPTION + Retrieves cluster node information including manager and controller nodes. + .EXAMPLE + Get-NSXTClusterNode + .EXAMPLE + Get-NSXTClusterNode -node_id "Node Id" + .EXAMPLE + Get-NSXTClusterNode -name "Name" +#> + + Param ( + [parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] + [Alias("Id")] + [string]$node_id, + [parameter(Mandatory=$false)] + [string]$name + ) + + begin + { + $NSXTClusterNodesService = Get-NsxtService -Name "com.vmware.nsx.cluster.nodes" + + class NSXTClusterNode { + [string]$Name + [string]$node_id + hidden [array]$Tags = [System.Collections.Generic.List[string]]::new() + hidden [string]$controller_role + hidden [array]$manager_role + [string]$protection + [string]$appliance_mgmt_listen_addr + hidden [string]$external_id + hidden [string]$description + [string]$role + } + } + + Process + { + if($node_id) { + $NSXTThings = $NSXTClusterNodesService.get($node_id) + } else { + if ($name) { + $NSXTClusterNodes = $NSXTClusterNodesService.list().results | where {$_.display_name -eq $name} + } + else { + $NSXTClusterNodes = $NSXTClusterNodesService.list().results + } + } + + foreach ($NSXTClusterNode in $NSXTClusterNodes) { + + $results = [NSXTClusterNode]::new() + $results.Name = $NSXTClusterNode.display_name; + $results.node_id = $NSXTClusterNode.Id; + $results.Tags = $NSXTClusterNode.tags; + $results.controller_role = $NSXTClusterNode.controller_role; + $results.manager_role = $NSXTClusterNode.manager_role; + $results.protection = $NSXTClusterNode.protection; + $results.appliance_mgmt_listen_addr = $NSXTClusterNode.appliance_mgmt_listen_addr; + $results.external_id = $NSXTClusterNode.external_id; + $results.description = $NSXTClusterNode.description + + if ($NSXTClusterNode.manager_role -ne $null) { + $results.role = "Manager" + } + elseif ($NSXTClusterNode.controller_role -ne $null) { + $results.role = "Controller" + } + + $results + } + } +} + +# Working Set Functions +Function Set-NSXTLogicalRouter { + <# + .Synopsis + Creates a Logical Router + .DESCRIPTION + Create a TIER0 or TIER1 logical router + .EXAMPLE + Set-NSXTLogicalRouter -display_name "Name" -high_availability_mode "ACTIVE_STANDBY" -router_type "TIER1" + .EXAMPLE + Set-NSXTLogicalRouter -display_name "Name" -high_availability_mode "ACTIVE_ACTIVE" -router_type "TIER0" -edge_cluster_id "Edge Cluster ID" + .EXAMPLE + Set-NSXTLogicalRouter -display_name "Name" -high_availability_mode "ACTIVE_STANDBY" -router_type "TIER1" -description "this is my new tier1 lr" +#> + + [CmdletBinding(SupportsShouldProcess=$true, + ConfirmImpact='Medium')] + + # Paramameter Set variants will be needed Multicast & Broadcast Traffic Types as well as VM & Logical Port Types + Param ( + [parameter(Mandatory=$false, + ParameterSetName='TIER0')] + [parameter(Mandatory=$false, + ParameterSetName='TIER1')] + [string]$description, + + [parameter(Mandatory=$true, + ParameterSetName='TIER0')] + [parameter(Mandatory=$true, + ParameterSetName='TIER1')] + [string]$display_name, + + [parameter(Mandatory=$true, + ParameterSetName='TIER0')] + [parameter(Mandatory=$true, + ParameterSetName='TIER1')] + [ValidateSet("ACTIVE_ACTIVE","ACTIVE_STANDBY")] + [string]$high_availability_mode, + + [parameter(Mandatory=$true, + ParameterSetName='TIER0')] + [parameter(Mandatory=$true, + ParameterSetName='TIER1')] + [ValidateSet("TIER0","TIER1")] + [string]$router_type, + + [parameter(Mandatory=$true, + ParameterSetName='TIER0')] + [string]$edge_cluster_id + ) + + Begin + { + if (-not $global:DefaultNsxtServers.isconnected) + { + try + { + Connect-NsxtServer -Menu -ErrorAction Stop + } + + catch + { + throw "Could not connect to an NSX-T Manager, please try again" + } + } + + $NSXTLogicalRouterService = Get-NsxtService -Name "com.vmware.nsx.logical_routers" + } + + Process + { + $logical_router_request = $NSXTLogicalRouterService.help.create.logical_router.Create() + + $logical_router_request.display_name = $display_name + $logical_router_request.description = $description + $logical_router_request.router_type = $router_type + $logical_router_request.high_availability_mode = $high_availability_mode + $logical_router_request.resource_type = "LogicalRouter" + $logical_router_request.failover_mode = "NON_PREEMPTIVE" + + if ($edge_cluster_id) { + $logical_router_request.edge_cluster_id = $edge_cluster_id + } + + try + { + # Should process + if ($pscmdlet.ShouldProcess($logical_router_request.display_name, "Create logical router")) + { + $NSXTLogicalRouter = $NSXTLogicalRouterService.create($logical_router_request) + } + } + + catch + { + throw $Error[0].Exception.ServerError.data + # more error data found in the NSX-T Manager /var/log/vmware/nsx-manager.log file. + } + + $NSXTLogicalRouter + } +} + +Function Set-NSXTLogicalSwitch { + <# + .Synopsis + Creates a Logical Switch + .DESCRIPTION + Creates a Logical Switch with a number of required parameters. IP Pool is necessary even for an overlay logical switch + .EXAMPLE + Set-NSXTLogicalSwitch -display_name "Name" -transport_zone_id "TP Zone ID" + .EXAMPLE + Set-NSXTLogicalSwitch -display_name "Name" -transport_zone_id "TP Zone ID" -admin_state "UP" -replication_mode "MTEP" -ip_pool_id "IP Pool Name" +#> + + [CmdletBinding(SupportsShouldProcess=$true, + ConfirmImpact='Medium')] + + # Paramameter Set variants will be needed Multicast & Broadcast Traffic Types as well as VM & Logical Port Types + Param ( + [parameter(Mandatory=$false)] + [string]$description, + + [parameter(Mandatory=$true)] + [string]$display_name, + + [parameter(Mandatory=$true)] + [string]$transport_zone_id, + + [parameter(Mandatory=$true)] + [ValidateSet("UP","DOWN")] + [string]$admin_state, + + [parameter(Mandatory=$false)] + [ValidateSet("MTEP","SOURCE")] + [string]$replication_mode, + + [parameter(Mandatory=$true)] + [string]$ip_pool_id + ) + + Begin + { + if (-not $global:DefaultNsxtServers.isconnected) + { + try + { + Connect-NsxtServer -Menu -ErrorAction Stop + } + + catch + { + throw "Could not connect to an NSX-T Manager, please try again" + } + } + + $NSXTLogicalSwitchService = Get-NsxtService -Name "com.vmware.nsx.logical_switches" + } + + Process + { + $logical_switch_request = $NSXTLogicalSwitchService.help.create.logical_switch.Create() + + $logical_switch_request.display_name = $display_name + $logical_switch_request.description = $description + $logical_switch_request.admin_state = $admin_state + $logical_switch_request.transport_zone_id = $transport_zone_id + $logical_switch_request.resource_type = "LogicalSwitch" + $logical_switch_request.replication_mode = $replication_mode + $logical_switch_request.ip_pool_id = $ip_pool_id + + try + { + # Should process + if ($pscmdlet.ShouldProcess($logical_switch_request.display_name, "Create logical switch")) + { + $NSXTLogicalSwitch = $NSXTLogicalSwitchService.create($logical_switch_request) + } + + } + + catch + { + throw $Error[0].Exception.ServerError.data + # more error data found in the NSX-T Manager /var/log/vmware/nsx-manager.log file. + } + + $NSXTLogicalSwitch + } +} + +Function Set-NSXTIPAMIPBlock { + <# + .Synopsis + Creates an IPAM IP Block + .DESCRIPTION + Creates a IPAM IP Block with a cidr parameter. + .EXAMPLE + Set-NSXTIPAMIPBlock -name "IPAM Block Name" -cidr "192.168.0.0/24" +#> + + [CmdletBinding(SupportsShouldProcess=$true, + ConfirmImpact='Medium')] + + # Paramameter Set variants will be needed Multicast & Broadcast Traffic Types as well as VM & Logical Port Types + Param ( + [parameter(Mandatory=$false)] + [string]$description, + + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string]$display_name, + + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string]$cidr + ) + + Begin + { + if (-not $global:DefaultNsxtServers.isconnected) + { + try + { + Connect-NsxtServer -Menu -ErrorAction Stop + } + + catch + { + throw "Could not connect to an NSX-T Manager, please try again" + } + } + + $NSXTIPAMIPBlockService = Get-NsxtService -Name "com.vmware.nsx.pools.ip_blocks" + } + + Process + { + $IPAMIPBlock_request = $NSXTIPAMIPBlockService.help.create.ip_block.Create() + + $IPAMIPBlock_request.display_name = $display_name + $IPAMIPBlock_request.description = $description + $IPAMIPBlock_request.resource_type = "IpBlock" + $IPAMIPBlock_request.cidr = $cidr + + + try + { + # Should process + if ($pscmdlet.ShouldProcess($ip_pool.display_name, "Create IP Pool")) + { + $NSXTIPAMIPBlock = $NSXTIPAMIPBlockService.create($IPAMIPBlock_request) + } + } + + catch + { + throw $Error[0].Exception.ServerError.data + # more error data found in the NSX-T Manager /var/log/vmware/nsx-manager.log file. + } + + $NSXTIPAMIPBlock + } +} + +Function Set-NSXTIPPool { + <# + .Synopsis + Creates an IP Pool + .DESCRIPTION + Creates a IP Pool with a number of required parameters. Supported IP formats include 192.168.1.1, 192.168.1.1-192.168.1.100, 192.168.0.0/24 + .EXAMPLE + Set-NSXTIPPool -display_name "Pool Name" -allocation_start "192.168.1.2" -allocation_end "192.168.1.100" -cidr "192.168.1.0/24" + .EXAMPLE + Set-NSXTIPPool -display_name "Test Pool Name" -allocation_start "192.168.1.2" -allocation_end "192.168.1.100" -cidr "192.168.1.0/24" -dns_nameservers "192.168.1.1" -gateway_ip "192.168.1.1" -dns_suffix "evil corp" +#> + + [CmdletBinding(SupportsShouldProcess=$true, + ConfirmImpact='High')] + + # Paramameter Set variants will be needed Multicast & Broadcast Traffic Types as well as VM & Logical Port Types + Param ( + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string]$display_name, + + [parameter(Mandatory=$false)] + [string]$description, + + [parameter(Mandatory=$false)] + [string]$dns_nameservers, + + [parameter(Mandatory=$false)] + [string]$dns_suffix, + + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string]$allocation_start, + + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string]$allocation_end, + + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string]$cidr, + + [parameter(Mandatory=$false)] + [string]$gateway_ip + ) + + Begin + { + if (-not $global:DefaultNsxtServers.isconnected) + { + try + { + Connect-NsxtServer -Menu -ErrorAction Stop + } + + catch + { + throw "Could not connect to an NSX-T Manager, please try again" + } + } + + $NSXTIPPoolService = Get-NsxtService -Name "com.vmware.nsx.pools.ip_pools" + + # Classes unused - part of early testing + class allocation_ranges { + [string]$start + [string]$end + #$self + } + + class subnets { + [array]$allocation_ranges = [allocation_ranges]::new() + [array]$dns_nameservers + [string]$dns_suffix + [string]$cidr + [string]$gateway_ip + #hidden $self + } + + class ip_pool { + [string]$display_name + [string]$description + [string]$resource_type = 'IpPool' + [long]$revision = '0' + [array]$subnets = [subnets]::new() + hidden $pool_usage + hidden [array]$tags + # hidden $self + hidden $links + } + } + + Process + { + $sample_ip_pool = $NSXTIPPoolService.help.create.ip_pool.Create() + $sample_ip_pool.subnets = @($NSXTIPPoolService.help.create.ip_pool.subnets.Create()) + $sample_ip_pool.subnets = @($NSXTIPPoolService.help.create.ip_pool.subnets.Element.Create()) + $sample_ip_pool.subnets[0].allocation_ranges = @($NSXTIPPoolService.help.create.ip_pool.subnets.Element.allocation_ranges.create()) + $sample_ip_pool.subnets[0].allocation_ranges = @($NSXTIPPoolService.help.create.ip_pool.subnets.Element.allocation_ranges.element.create()) + + #Remove buggy self object + $ip_pool = $sample_ip_pool | select -Property * -ExcludeProperty self + $ip_pool.subnets[0] = $sample_ip_pool.subnets[0] | select -Property * -ExcludeProperty self + $ip_pool.subnets[0].allocation_ranges[0] = $sample_ip_pool.subnets[0].allocation_ranges[0] | select -Property * -ExcludeProperty self + + # Assign objects + $ip_pool.display_name = $display_name + $ip_pool.description = $description + $ip_pool.resource_type = "IpPool" + $ip_pool.subnets[0].dns_nameservers = @($dns_nameservers) + $ip_pool.subnets[0].dns_suffix = $dns_suffix + $ip_pool.subnets[0].allocation_ranges[0].start = $allocation_start + $ip_pool.subnets[0].allocation_ranges[0].end = $allocation_end + $ip_pool.subnets[0].cidr = $cidr + $ip_pool.subnets[0].gateway_ip = $gateway_ip + $ip_pool.revision = 0 + $ip_pool.tags = @() + + try + { + # Should process + if ($pscmdlet.ShouldProcess($ip_pool.display_name, "Create IP Pool")) + { + $NSXTIPPoolService.create($ip_pool) + } + } + + catch + { + $Error[0].Exception.ServerError.data + # more error data found in the NSX-T Manager /var/log/vmware/nsx-manager.log file; grep POOL-MGMT + throw + } + } +} + +# Remove functions +Function Remove-NSXTIPAMIPBlock { + <# + .Synopsis + Removes an IPAM IP Block + .DESCRIPTION + Removes a IPAM IP Block with a block_id parameter. + .EXAMPLE + Remove-NSXTIPAMIPBlock -block_id "id" + .EXAMPLE + Get-NSXTIPAMIPBlock | where name -eq "IPAM Test2" | Remove-NSXTIPAMIPBlock +#> + + [CmdletBinding(SupportsShouldProcess=$true, + ConfirmImpact='High')] + + Param ( + [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] + [ValidateNotNullOrEmpty()] + [Alias("Id")] + [string]$block_id + ) + + Begin + { + if (-not $global:DefaultNsxtServers.isconnected) + { + try + { + Connect-NsxtServer -Menu -ErrorAction Stop + } + + catch + { + throw "Could not connect to an NSX-T Manager, please try again" + } + } + + $NSXTIPAMIPBlockService = Get-NsxtService -Name "com.vmware.nsx.pools.ip_blocks" + } + + Process + { + try + { + # Should process + if ($pscmdlet.ShouldProcess($block_id, "Delete IP Pool")) + { + $NSXTIPAMIPBlockService.delete($block_id) + } + } + + catch + { + throw $Error[0].Exception.ServerError.data + # more error data found in the NSX-T Manager /var/log/vmware/nsx-manager.log file. + } + } +} + +# Non-working Set Functions +Function Set-NSXTTraceFlow { + <# + .Synopsis + Creates a TraceFlow + .DESCRIPTION + Create a TraceFlow for later observation. + .EXAMPLE + Set-NSXTTraceFlow -transport_type "UNICAST" -lport_id "LP ID" -src_ip "IP Address" -src_mac "MAC" -dst_ip "IP Address" -dst_mac "MAC" + .EXAMPLE + Set-NSXTTraceFlow -transport_type "UNICAST" -lport_id "LP ID" -src_ip "IP Address" -src_mac "MAC" -dst_ip "IP Address" -dst_mac "MAC" | Get-NSXTTraceFlow + .EXAMPLE + Set-NSXTTraceFlow -transport_type "UNICAST" -lport_id "LP ID" -src_ip "IP Address" -src_mac "MAC" -dst_ip "IP Address" -dst_mac "MAC" | Get-NSXTTraceFlow | Get-NSXTTraceFlowObservations +#> + + [CmdletBinding(SupportsShouldProcess=$true, + ConfirmImpact='Medium')] + + # Paramameter Set variants will be needed Multicast & Broadcast Traffic Types as well as VM & Logical Port Types + Param ( + [parameter(Mandatory=$true, + ParameterSetName='Parameter Set VM Type')] + [ValidateSet("UNICAST")] + [string] + $transport_type = "UNICAST", + [parameter(Mandatory=$true, + ValueFromPipeline=$true, + ParameterSetName='Parameter Set VM Type')] + [ValidateNotNullOrEmpty()] + #[ValidateScript({Get-NSXTLogicalPort -Id $_}] + [string] + $lport_id, + [parameter(Mandatory=$true, + ValueFromPipeline=$true, + ParameterSetName='Parameter Set VM Type')] + [ValidateNotNullOrEmpty()] + [ValidateScript({$_ -match [IPAddress]$_})] + [string] + $src_ip, + [parameter(Mandatory=$true, + ValueFromPipeline=$true, + ParameterSetName='Parameter Set VM Type')] + [ValidateNotNullOrEmpty()] + [ValidateScript({$pattern = '^(([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2}))|(([0-9A-Fa-f]{2}[-]){5}([0-9A-Fa-f]{2}))$' + if ($_ -match ($pattern -join '|')) {$true} else { + throw "The argument '$_' does not match a valid MAC address format." + } + })] + [string] + $src_mac, + [parameter(Mandatory=$true, + ValueFromPipeline=$true, + ParameterSetName='Parameter Set VM Type')] + [ValidateNotNullOrEmpty()] + [ValidateScript({$_ -match [IPAddress]$_ })] + [string] + $dst_ip, + [parameter(Mandatory=$true, + ValueFromPipeline=$true, + ParameterSetName='Parameter Set VM Type')] + [ValidateNotNullOrEmpty()] + [ValidateScript({$pattern = '^(([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2}))|(([0-9A-Fa-f]{2}[-]){5}([0-9A-Fa-f]{2}))$' + if ($_ -match ($pattern -join '|')) {$true} else { + throw "The argument '$_' does not match a valid MAC address format." + } + })] + [string] + $dst_mac) + + Begin + { + if (-not $global:DefaultNsxtServers.isconnected) + { + + try + { + Connect-NsxtServer -Menu -ErrorAction Stop + } + + catch + { + throw "Could not connect to an NSX-T Manager, please try again" + } + } + + $NSXTraceFlowsService = Get-NsxtService -Name "com.vmware.nsx.traceflows" + + # Comment out custom classes + <# + class ip_header { + [string]$src_ip + [string]$dst_ip + } + + class eth_header { + [string]$src_mac + [string]$dst_mac + } + + class packet_data { + [boolean]$routed + [ValidateSet("UNICAST","BROADCAST","MULTICAST","UNKNOWN")] + [string]$transport_type + [ValidateSet("BINARYPACKETDATA","FIELDSPACKETDATA")] + [string]$resource_type + [long]$frame_size + [eth_header]$eth_header = [eth_header]::new() + [ip_header]$ip_header = [ip_header]::new() + + packet_data(){ + $this.routed = 'true' + $this.transport_type = 'UNICAST' + $this.resource_type = 'FieldsPacketData' + } + } + + class traceflow_request { + [string]$lport_id + [long]$timeout + [packet_data]$packet = [packet_data]::new() + + traceflow_request(){ + $this.timeout = '15000' + } + } +#> + } + + Process + { + $traceflow_request = $NSXTraceFlowsService.Help.create.traceflow_request.Create() + + $traceflow_request.lport_id = $lport_id + $traceflow_request.packet.transport_type = $transport_type + + $eth_header = [ordered]@{'src_mac' = $src_mac;'eth_type' = '2048';'dst_mac' = $dst_mac} + $ip_header = [ordered]@{src_ip = $src_ip;protocol = '1';ttl = '64';dst_ip = $dst_ip} + $traceflow_request.packet | Add-Member -NotePropertyMembers $eth_header -TypeName eth_header + $traceflow_request.packet | Add-Member -NotePropertyMembers $ip_header -TypeName ip_header + + try + { + # Should process + if ($pscmdlet.ShouldProcess($traceflow_request.lport_id, "Create traceflow")) + { + # This does not work, bug report submitted to PowerCLI team + $NSXTraceFlow = $NSXTraceFlowService.create($traceflow_request) + } + } + + catch + { + throw $Error[0].Exception.ServerError.data + # more error data found in the NSX-T Manager /var/log/vmware/nsx-manager.log file. Filter by MONITORING. + } + + $NSXTraceFlow + } +} + + + +########################### +# # +# TEMPLATES!! # +# # +########################### + +# Get Template +Function Get-NSXTThingTemplate { + <# + .Synopsis + Retrieves the THING information + .DESCRIPTION + Retrieves THING information for a single or multiple ports. Execute with no parameters to get all ports, specify a PARAM if known. + .EXAMPLE + Get-NSXTThingTemplate + .EXAMPLE + Get-NSXTThingTemplate -param1 "LR Port Name" + .EXAMPLE + Get-NSXTThingTemplate -param2 "LR Name" + .EXAMPLE + Get-NSXTThingTemplate -param2 (Get-NSXTLogicalRouter | where name -eq "LR Name") +#> + + Param ( + [parameter(Mandatory=$false,ValueFromPipelineByPropertyName=$true)] + [Alias("Id")] + [string]$Thing_id, + [parameter(Mandatory=$false)] + [string]$name + ) + + begin + { + $NSXTThingsService = Get-NsxtService -Name "com.vmware.nsx.API.Thing" + + class NSXTThing { + [string]$Name + [string]$Thing1 + hidden [string]$Tags = [System.Collections.Generic.List[string]]::new() + [string]$Thing2 + #[ValidateSet("TIER0","TIER1")] + [string]$Thing3 + #[ValidateSet("ACTIVE_ACTIVE","ACTIVE_STANDBY","")] + [string]$Thing4 + #[ValidateSet("PREEMPTIVE","NON_PREEMPTIVE","")] + [string]$Thing5 + [string]$Thing6 + [string]$Thing7 + } + } + + Process + { + if($Thing_id) { + $NSXTThings = $NSXTThingsService.get($Thing_id) + } else { + if ($name) { + $NSXTThings = $NSXTThingsService.list().results | where {$_.display_name -eq $name} + } + else { + $NSXTThings = $NSXTThingsService.list().results + } + } + + foreach ($NSXTThing in $NSXTThings) { + + $results = [NSXTThing]::new() + $results.Name = $NSXTThing.display_name; + $results.Logical_router_id = $NSXTThing.Id; + $results.Tags = $NSXTThing.tags; + $results.thing1 = $NSXTThing.thing1; + $results.thing2 = $NSXTThing.thing2 + + $results + } + } +} + +# Set Template +Function Set-NSXTThingTemplate { + <# + .Synopsis + Creates a THING + .DESCRIPTION + Creates a THING with a number of required parameters. + .EXAMPLE + Set-NSXTThingTemplateh -param1 "Name" -param2 "TP Zone ID" + .EXAMPLE + Set-NSXTThingTemplateh -param1 "Name" -param2 "TP Zone ID" +#> + + [CmdletBinding(SupportsShouldProcess=$true, + ConfirmImpact='Medium')] + + # Paramameter Set variants will be needed Multicast & Broadcast Traffic Types as well as VM & Logical Port Types + Param ( + [parameter(Mandatory=$false)] + [string]$description, + + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string]$display_name, + + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string]$transport_zone_id, + + [parameter(Mandatory=$true)] + [ValidateSet("UP","DOWN")] + [string]$admin_state, + + [parameter(Mandatory=$false)] + [ValidateSet("MTEP","SOURCE")] + [string]$replication_mode, + + [parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string]$ip_pool_id + ) + + Begin + { + if (-not $global:DefaultNsxtServers.isconnected) + { + try + { + Connect-NsxtServer -Menu -ErrorAction Stop + } + + catch + { + throw "Could not connect to an NSX-T Manager, please try again" + } + } + + $NSXTTHINGService = Get-NsxtService -Name "com.vmware.nsx.THING" + } + + Process + { + $logical_THING_request = $NSXTTHINGService.help.create.logical_switch.Create() + + $logical_THING_request.display_name = $display_name + $logical_THING_request.description = $description + $logical_THING_request.admin_state = $admin_state + $logical_THING_request.transport_zone_id = $transport_zone_id + $logical_THING_request.resource_type = "LogicalSwitch" + $logical_THING_request.replication_mode = $replication_mode + $logical_THING_request.ip_pool_id = $ip_pool_id + + try + { + # Should process + if ($pscmdlet.ShouldProcess($ip_pool.display_name, "Create IP Pool")) + { + $NSXTTHING = $NSXTTHINGService.create($logical_THING_request) + } + } + + catch + { + throw $Error[0].Exception.ServerError.data + # more error data found in the NSX-T Manager /var/log/vmware/nsx-manager.log file. + } + + $NSXTTHING + } +} + +# Remove Template +Function Remove-NSXTThingTemplate { + <# + .Synopsis + Removes an IPAM IP Block + .DESCRIPTION + Removes a IPAM IP Block with a block_id parameter. + .EXAMPLE + Remove-NSXTIPAMIPBlock -block_id "id" +#> + + [CmdletBinding(SupportsShouldProcess=$true, + ConfirmImpact='High')] + + Param ( + [parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] + [ValidateNotNullOrEmpty()] + [Alias("Id")] + [string]$thing_id + ) + + Begin + { + if (-not $global:DefaultNsxtServers.isconnected) + { + try + { + Connect-NsxtServer -Menu -ErrorAction Stop + } + + catch + { + throw "Could not connect to an NSX-T Manager, please try again" + } + } + + $NSXTTHINGkService = Get-NsxtService -Name "com.vmware.nsx.THING" + } + + Process + { + try + { + # Should process + if ($pscmdlet.ShouldProcess($thing_id, "Delete IP Pool")) + { + $NSXTTHINGkService.delete($thing_id) + } + } + + catch + { + throw $Error[0].Exception.ServerError.data + # more error data found in the NSX-T Manager /var/log/vmware/nsx-manager.log file. + } + } +} + + + diff --git a/Modules/PSvLIMessage.psm1 b/Modules/PSvLIMessage/PSvLIMessage.psm1 similarity index 100% rename from Modules/PSvLIMessage.psm1 rename to Modules/PSvLIMessage/PSvLIMessage.psm1 diff --git a/Modules/PerVMEVC/PerVMEVC.psm1 b/Modules/PerVMEVC/PerVMEVC.psm1 new file mode 100644 index 0000000..05f8390 --- /dev/null +++ b/Modules/PerVMEVC/PerVMEVC.psm1 @@ -0,0 +1,192 @@ +function Get-VMEvcMode { +<# +.SYNOPSIS + Gathers information on the EVC status of a VM +.DESCRIPTION + Will provide the EVC status for the specified VM +.NOTES + Author: Kyle Ruddy, @kmruddy, thatcouldbeaproblem.com +.PARAMETER Name + VM name which the function should be ran against +.EXAMPLE + Get-VMEvcMode -Name vmName + Retreives the EVC status of the provided VM +#> +[CmdletBinding()] + param( + [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true)] + $Name + ) + + Process { + $evVM = @() + + if ($name -is [string]) {$evVM += Get-VM -Name $Name -ErrorAction SilentlyContinue} + elseif ($name -is [array]) { + + if ($name[0] -is [string]) { + $name | foreach { + $evVM += Get-VM -Name $_ -ErrorAction SilentlyContinue + } + } + elseif ($name[0] -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl]) {$evVM = $name} + + } + elseif ($name -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl]) {$evVM += $name} + + if ($evVM -eq $null) {Write-Warning "No VMs found."} + else { + $output = @() + foreach ($v in $evVM) { + + $report = "" | select Name,EVCMode + $report.Name = $v.Name + $report.EVCMode = $v.ExtensionData.Runtime.MinRequiredEVCModeKey + $output += $report + + } + + return $output + + } + + } + +} + +function Remove-VMEvcMode { +<# +.SYNOPSIS + Removes the EVC status of a VM +.DESCRIPTION + Will remove the EVC status for the specified VM +.NOTES + Author: Kyle Ruddy, @kmruddy, thatcouldbeaproblem.com +.PARAMETER Name + VM name which the function should be ran against +.EXAMPLE + Remove-VMEvcMode -Name vmName + Removes the EVC status of the provided VM +#> +[CmdletBinding()] + param( + [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true)] + $Name + ) + + Process { + $evVM = @() + $updateVM = @() + + if ($name -is [string]) {$evVM += Get-VM -Name $Name -ErrorAction SilentlyContinue} + elseif ($name -is [array]) { + + if ($name[0] -is [string]) { + $name | foreach { + $evVM += Get-VM -Name $_ -ErrorAction SilentlyContinue + } + } + elseif ($name[0] -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl]) {$evVM = $name} + + } + elseif ($name -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl]) {$evVM += $name} + + if ($evVM -eq $null) {Write-Warning "No VMs found."} + else { + foreach ($v in $evVM) { + + if (($v.HardwareVersion -ge 'vmx-14' -and $v.PowerState -eq 'PoweredOff') -or ($v.Version -ge 'v14' -and $v.PowerState -eq 'PoweredOff')) { + + $v.ExtensionData.ApplyEvcModeVM_Task($null, $true) | Out-Null + $updateVM += $v.Name + + } + else {Write-Warning $v.Name + " does not have the minimum requirements of being Hardware Version 14 and powered off."} + + } + + if ($updateVM) { + + Start-Sleep -Seconds 2 + Get-VMEvcMode -Name $updateVM + + } + + } + + } + +} + +function Set-VMEvcMode { +<# +.SYNOPSIS + Configures the EVC status of a VM +.DESCRIPTION + Will configure the EVC status for the specified VM +.NOTES + Author: Kyle Ruddy, @kmruddy, thatcouldbeaproblem.com +.PARAMETER Name + VM name which the function should be ran against +.PARAMETER EvcMode + The EVC Mode key which should be set +.EXAMPLE + Set-VMEvcMode -Name vmName -EvcMode intel-sandybridge + Configures the EVC status of the provided VM to be 'intel-sandybridge' +#> +[CmdletBinding()] + param( + [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true)] + $Name, + [Parameter(Mandatory=$true,Position=1)] + [ValidateSet("intel-merom","intel-penryn","intel-nehalem","intel-westmere","intel-sandybridge","intel-ivybridge","intel-haswell","intel-broadwell","intel-skylake","amd-rev-e","amd-rev-f","amd-greyhound-no3dnow","amd-greyhound","amd-bulldozer","amd-piledriver","amd-steamroller","amd-zen")] + $EvcMode + ) + + Process { + $evVM = @() + $updateVM = @() + + if ($name -is [string]) {$evVM += Get-VM -Name $Name -ErrorAction SilentlyContinue} + elseif ($name -is [array]) { + + if ($name[0] -is [string]) { + $name | foreach { + $evVM += Get-VM -Name $_ -ErrorAction SilentlyContinue + } + } + elseif ($name[0] -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl]) {$evVM = $name} + + } + elseif ($name -is [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl]) {$evVM += $name} + + if ($evVM -eq $null) {Write-Warning "No VMs found."} + else { + + $si = Get-View ServiceInstance + $evcMask = $si.Capability.SupportedEvcMode | where-object {$_.key -eq $EvcMode} | select -ExpandProperty FeatureMask + + foreach ($v in $evVM) { + + if (($v.HardwareVersion -ge 'vmx-14' -and $v.PowerState -eq 'PoweredOff') -or ($v.Version -ge 'v14' -and $v.PowerState -eq 'PoweredOff')) { + + $v.ExtensionData.ApplyEvcModeVM_Task($evcMask, $true) | Out-Null + $updateVM += $v.Name + + } + else {Write-Warning $v.Name + " does not have the minimum requirements of being Hardware Version 14 and powered off."} + + } + + if ($updateVM) { + + Start-Sleep -Seconds 2 + Get-VMEvcMode -Name $updateVM + + } + + } + + } + +} diff --git a/Modules/ProactiveHA/ProactiveHA.psm1 b/Modules/ProactiveHA/ProactiveHA.psm1 new file mode 100644 index 0000000..ea4e92f --- /dev/null +++ b/Modules/ProactiveHA/ProactiveHA.psm1 @@ -0,0 +1,468 @@ +Function New-PHAProvider { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to register a new Proactive HA Provider with vCenter Server + .PARAMETER ProviderName + Name of ProactiveHA Provider + .PARAMETER ComponentType + Name of a supported ComponentType that ProactiveHA supports (Fan, Memory, Network, Power or Storage) + .PARAMETER ComponentDescription + Description of the health check for the given component + .PARAMETER ComponentId + Unique identifier for the given component within a ProactiveHA Provider + .EXAMPLE + New-PHAProvider -ProviderName "virtuallyGhetto" -ComponentType Power -ComponentDescription "Simulated ProactiveHA Provider" -ComponentId "Power" +#> + param( + [Parameter(Mandatory=$true)][String]$ProviderName, + [Parameter(Mandatory=$true)][ValidateSet("Fan","Memory","Network","Power","Storage")][String]$ComponentType, + [Parameter(Mandatory=$true)][String]$ComponentDescription, + [Parameter(Mandatory=$true)][String]$ComponentId + ) + Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" + Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" + Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" + Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" + + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + $healthInfo = [VMware.Vim.HealthUpdateInfo] @{ + ComponentType = $ComponentType + description = $ComponentDescription + Id = $ComponentId + } + + try { + Write-Host "`nRegistering new Proactive HA Provider $ProviderName ..." + $providerId = $healthManager.RegisterHealthUpdateProvider($ProviderName,$healthInfo) + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } +} + +Function Get-PHAProvider { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to return list of all Proactive HA Providers registered with vCenter Server + .EXAMPLE + Get-PHAProvider +#> + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + $healthProviderResults = @() + $hpIDs = $healthManager.QueryProviderList() + + foreach ($hpID in $hpIDs) { + $hpName = $healthManager.QueryProviderName($hpID) + $hpConfig = $healthManager.QueryHealthUpdateInfos($hpID) + + $hp = [pscustomobject] @{ + ProviderName = $hpName + ProviderID = $hpID + ComponentType = $hpConfig.componentType + ComponentID = $hpConfig.id + Description = $hpConfig.description + } + $healthProviderResults+=$hp + } + $healthProviderResults +} + +Function Remove-PHAProvider { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to remove a registered Proactive HA Provider from vCenter Server + .PARAMETER ProviderId + The ProactiveHA provider ID (retrieved from Get-PHAProvider) to unregister + .EXAMPLE + Remove-PHAProvider -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" +#> + param( + [Parameter(Mandatory=$true)][String]$ProviderId + ) + + Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" + Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" + Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" + Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" + + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + try { + Write-Host "`nUnregistering Proactive HA Provider $ProviderId ... " + $healthManager.UnregisterHealthUpdateProvider($providerId) + } catch { + if($Error[0].Exception.InnerException.MethodFault.getType().Name -eq "InvalidState") { + Write-host -ForegroundColor Red "The Proactive HA Provider is still in use, please disable it before unregistering" + } else { + Write-host -ForegroundColor Red $Error[0].Exception + } + } +} + +Function Set-PHAConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to enable/disable Proactive HA for vSphere Cluster + .PARAMETER Cluster + Name of the vSphere Cluster to enable Proactive HA + .PARAMETER ProviderId + Proactive HA Provider ID to enable in vSphere Cluster + .PARAMETER ClusterMode + Whether Proactive HA should be "Automated" or "Manual" for actions it will take + .PARAMETER ModerateRemediation + Type of operation (Maintenance Mode or Quaratine Mode) to perform when a Moderate issue is observed + .PARAMETER SevereRemediation + Type of operation (Maintenance Mode or Quaratine Mode) to perform when a Severe issue is observed + .EXAMPLE + Set-PHAConfig -Cluster VSAN-Cluster -Enabled -ClusterMode Automated -ModerateRemediation QuarantineMode -SevereRemediation QuarantineMode -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" + .EXAMPLE + Set-PHAConfig -Cluster VSAN-Cluster -Disabled -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" +#> + param( + [Parameter(Mandatory=$true)][String]$ProviderId, + [Parameter(Mandatory=$true)][String]$Cluster, + [Parameter(Mandatory=$false)][ValidateSet("Automated","Manual")]$ClusterMode="Manual", + [Parameter(Mandatory=$false)][ValidateSet("MaintenanceMode","QuarantineMode")]$ModerateRemediation="QuarantineMode", + [Parameter(Mandatory=$false)][ValidateSet("MaintenanceMode","QuarantineMode")]$SevereRemediation="QuarantineMode", + [Switch]$Enabled, + [Switch]$Disabled + ) + + $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,Host,ConfigurationEx -Filter @{"Name" = $Cluster} + + if($ClusterView -eq $null) { + Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." + break + } + + $vmhosts = $ClusterView.host + + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + if($Enabled) { + try { + $entities = @() + foreach ($vmhost in $vmhosts) { + if(-not $healthManager.HasMonitoredEntity($ProviderId,$vmhost)) { + $entities += $vmhost + } + } + + Write-Host "Enabling Proactive HA monitoring for all ESXi hosts in cluster ..." + $healthManager.AddMonitoredEntities($ProviderId,$entities) + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } + + try { + $healthProviders = @() + + # Make sure not to remove existing ProactiveHA providers + if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { + $currentHPs = $ClusterView.ConfigurationEx.infraUpdateHaConfig.providers + foreach ($currentHP in $currentHPs) { + $healthProviders+=$currentHP + } + if(-not ($healthProviders -contains $ProviderID)) { + $healthProviders+=$ProviderId + } + } else { + $healthProviders+=$ProviderId + } + + $PHASpec = [VMware.Vim.ClusterInfraUpdateHaConfigInfo] @{ + enabled = $true + behavior = $ClusterMode + moderateRemediation = $ModerateRemediation + severeRemediation = $SevereRemediation + providers = $healthProviders + } + + $spec = [VMware.Vim.ClusterConfigSpecEx] @{ + infraUpdateHaConfig = $PHASpec + } + + Write-Host "Enabling Proactive HA Provider $ProviderId on $Cluster ..." + $task = $ClusterView.ReconfigureComputeResource_Task($spec,$True) + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task | Out-Null + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } + } + + if($Disabled) { + foreach ($vmhost in $vmhosts) { + if($vmhost.runtime.inQuarantineMode) { + Write-Host -ForegroundColor Red $vmhost.name " is currently still in Quaratine Mode, please remediate this before disabling Proactive HA" + break + } + } + + try { + $healthProviders = @() + + # Make sure not to remove existing ProactiveHA providers + if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { + $currentHPs = $ClusterView.ConfigurationEx.infraUpdateHaConfig.providers + foreach ($currentHP in $currentHPs) { + if($currentHP -ne $ProviderId) { + $healthProviders+=$currentHP + } + } + } + + $PHASpec = [VMware.Vim.ClusterInfraUpdateHaConfigInfo] @{ + enabled = $true + behavior = $ClusterMode + moderateRemediation = $ModerateRemediation + severeRemediation = $SevereRemediation + providers = $healthProviders + } + + $spec = [VMware.Vim.ClusterConfigSpecEx] @{ + infraUpdateHaConfig = $PHASpec + } + + Write-Host "Disabling Proactive HA Provider $ProviderId on $Cluster ..." + $task = $ClusterView.ReconfigureComputeResource_Task($spec,$True) + $task1 = Get-Task -Id ("Task-$($task.value)") + $task1 | Wait-Task | Out-Null + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } + + $ClusterView.UpdateViewData() + + try { + $entities = @() + foreach ($vmhost in $vmhosts) { + if($healthManager.HasMonitoredEntity($ProviderId,$vmhost)) { + $entities += $vmhost + } + } + + Write-Host "Disabling Proactive HA monitoring for all ESXi hosts in cluster ..." + $healthManager.RemoveMonitoredEntities($ProviderId,$entities) + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } + } +} + +Function Get-PHAConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to retrieve Proactive HA configuration for a vSphere Cluster + .PARAMETER Cluster + Name of the vSphere Cluster to check Proactive HA configuration + .EXAMPLE + Get-PHAConfig -Cluster VSAN-Cluster +#> + param( + [Parameter(Mandatory=$true)][String]$Cluster + ) + + $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,ConfigurationEx -Filter @{"Name" = $Cluster} + + if($ClusterView -eq $null) { + Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." + break + } + + if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + $phSettings = $ClusterView.ConfigurationEx.InfraUpdateHaConfig + $providers = $ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers + $healthProviders = @() + foreach ($provider in $providers) { + $providerName = $healthManager.QueryProviderName($provider) + $healthProviders+=$providerName + } + + $pHAConfig = [pscustomobject] @{ + Enabled = $phSettings.Enabled + ClusterMode = $phSettings.behavior + ModerateRemediation = $phSettings.ModerateRemediation + SevereRemediation = $phSettings.SevereRemediation + HealthProviders = $healthProviders + } + $pHAConfig + } else { + Write-Host "Proactive HA has not been configured on this vSphere Cluster" + } +} + +Function Get-PHAHealth { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to retrieve the Proactive HA health info for all ESXi hosts in vSphere Cluster + .PARAMETER Cluster + Name of the vSphere Cluster to check Proactive HA health information + .EXAMPLE + Get-PHAHealth -Cluster VSAN-Cluster +#> + param( + [Parameter(Mandatory=$true)][String]$Cluster + ) + + $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,ConfigurationEx -Filter @{"Name" = $Cluster} + + if($ClusterView -eq $null) { + Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." + break + } + + if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + $providers = $ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers + + foreach ($provider in $providers) { + $providerName = $healthManager.QueryProviderName($provider) + $healthUpdates = $healthManager.QueryHealthUpdates($provider) + + $healthResults = @() + Write-Host -NoNewline -ForegroundColor Magenta "Health summary for Proactive HA Provider $providerName`:`n" + foreach ($healthUpdate in $healthUpdates) { + $vmhost = Get-View $healthUpdate.Entity + + $hr = [PSCustomObject] @{ + Entity = $vmhost.name + Status = $healthUpdate.status + HealthComponentId = $healthUpdate.HealthUpdateInfoId + HealthUpdateId = $healthUpdate.Id + Remediation = $healthUpdate.Remediation + } + $healthResults+=$hr + } + $healthResults + } + } else { + Write-Host "Proactive HA has not been configured on this vSphere Cluster" + } +} + +Function New-PHASimulation { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + Function to return VCHA Configuration + .PARAMETER ProviderId + The Proactive HA Provider ID that you like to simulate a health update from + .PARAMETER EsxiHost + The name of ESXi host to update the health on + .PARAMETER Component + The name of the matching component ID from Proactive HA Provider to simulate a health update from + .PARAMETER HealthStatus + The health value (green, yellow or red) for the given simulated health Update + .PARAMETER Remediation + The remediation message associated with simulated health update + .EXAMPLE + New-PHASimulation -EsxiHost vesxi65-4.primp-industries.com -Component Power -HealthStatus green -Remediation "" -ProviderId "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" + .EXAMPLE + New-PHASimulation -EsxiHost vesxi65-4.primp-industries.com -Component Power -HealthStatus red -Remediation "Please replace my virtual PSU" -ProviderId "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" +#> + param( + [Parameter(Mandatory=$true)][String]$ProviderId, + [Parameter(Mandatory=$true)][String]$EsxiHost, + [Parameter(Mandatory=$true)][String]$Component, + [Parameter(Mandatory=$true)][ValidateSet("green","red","yellow")][String]$HealthStatus, + [Parameter(Mandatory=$false)][String]$Remediation + ) + + Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" + Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" + Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" + Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" + + $vmhost = Get-View -ViewType HostSystem -Property Name -Filter @{"name" = $EsxiHost} + + if($vmhost -eq $null) { + Write-Host -ForegroundColor Red "`nUnable to find ESXi host $EsxiHost ..." + break + } + + $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager + + # Randomly generating an ID for Health Update + # In general, you would want to generate a specific ID + # which can be referenced between ProactiveHA Provider + # and VMware logs for troubleshooting purposes + $HealthUpdateID = "vghetto-" + (Get-Random -Minimum 1 -Maximum 100000) + + # All other Health Status can have a remediation message + # but for green, it must be an empty string or API call will fail + if($HealthStatus -eq "green") { + $Remediation = "" + } + + $healthUpdate = [VMware.Vim.HealthUpdate] @{ + Entity = $vmhost.moref + HealthUpdateInfoId = $Component + Id = $HealthUpdateId + Status = $HealthStatus + Remediation = $Remediation + } + + try { + Write-Host "`nSimulating Proactive HA Health Update to ..." + Write-Host "`tHost: $EsxiHost " + Write-Host -NoNewline "`tStatus: " + Write-Host -ForegroundColor $HealthStatus "$HealthStatus" + Write-Host "`tRemediation Messsage: $Remediation" + $healthManager.PostHealthUpdates($providerId,$healthUpdate) + } catch { + Write-host -ForegroundColor Red $Error[0].Exception + } +} \ No newline at end of file diff --git a/Modules/Recommend-Sizing.psm1 b/Modules/Recommend-Sizing/Recommend-Sizing.psm1 similarity index 100% rename from Modules/Recommend-Sizing.psm1 rename to Modules/Recommend-Sizing/Recommend-Sizing.psm1 diff --git a/Modules/SRM/.gitattributes b/Modules/SRM/.gitattributes new file mode 100644 index 0000000..75f1064 --- /dev/null +++ b/Modules/SRM/.gitattributes @@ -0,0 +1 @@ +*.psd1 diff diff --git a/Modules/SRM/.gitignore b/Modules/SRM/.gitignore new file mode 100644 index 0000000..6f66c74 --- /dev/null +++ b/Modules/SRM/.gitignore @@ -0,0 +1 @@ +*.zip \ No newline at end of file diff --git a/Modules/SRM/Examples/ReportConfiguration.ps1 b/Modules/SRM/Examples/ReportConfiguration.ps1 new file mode 100644 index 0000000..f7a8036 --- /dev/null +++ b/Modules/SRM/Examples/ReportConfiguration.ps1 @@ -0,0 +1,167 @@ +# Depends on SRM Helper Methods - https://github.com/benmeadowcroft/SRM-Cmdlets +# It is assumed that the connection to VC and SRM Server have already been made + +Function Get-SrmConfigReportSite { + Param( + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + Get-SrmServer $SrmServer | + Format-Table -Wrap -AutoSize @{Label="SRM Site Name"; Expression={$_.ExtensionData.GetSiteName()} }, + @{Label="SRM Host"; Expression={$_.Name} }, + @{Label="SRM Port"; Expression={$_.Port} }, + @{Label="Version"; Expression={$_.Version} }, + @{Label="Build"; Expression={$_.Build} }, + @{Label="SRM Peer Site Name"; Expression={$_.ExtensionData.GetPairedSite().Name} } +} + +Function Get-SrmConfigReportPlan { + Param( + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + Get-SrmRecoveryPlan -SrmServer $SrmServer | %{ + $rp = $_ + $rpinfo = $rp.GetInfo() + $peerState = $rp.GetPeer().State + $pgs = Get-SrmProtectionGroup -RecoveryPlan $rp + $pgnames = $pgs | %{ $_.GetInfo().Name } + + $output = "" | select plan, state, peerState, groups + $output.plan = $rpinfo.Name + $output.state = $rpinfo.State + $output.peerState = $peerState + if ($pgnames) { + $output.groups = [string]::Join(",`r`n", $pgnames) + } else { + $output.groups = "NONE" + } + + $output + } | Format-Table -Wrap -AutoSize @{Label="Recovery Plan Name"; Expression={$_.plan} }, + @{Label="Recovery State"; Expression={$_.state} }, + @{Label="Peer Recovery State"; Expression={$_.peerState} }, + @{Label="Protection Groups"; Expression={$_.groups}} +} + + +Function Get-SrmConfigReportProtectionGroup { + Param( + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + Get-SrmProtectionGroup -SrmServer $SrmServer | %{ + $pg = $_ + $pginfo = $pg.GetInfo() + $pgstate = $pg.GetProtectionState() + $peerState = $pg.GetPeer().State + $rps = Get-SrmRecoveryPlan -ProtectionGroup $pg + $rpnames = $rps | %{ $_.GetInfo().Name } + + $output = "" | select name, type, state, peerState, plans + $output.name = $pginfo.Name + $output.type = $pginfo.Type + $output.state = $pgstate + $output.peerState = $peerState + if ($rpnames) { + $output.plans = [string]::Join(",`r`n", $rpnames) + } else { + $output.plans = "NONE" + } + + $output + } | Format-Table -Wrap -AutoSize @{Label="Protection Group Name"; Expression={$_.name} }, + @{Label="Type"; Expression={$_.type} }, + @{Label="Protection State"; Expression={$_.state} }, + @{Label="Peer Protection State"; Expression={$_.peerState} }, + @{Label="Recovery Plans"; Expression={$_.plans} } +} + + +Function Get-SrmConfigReportProtectedDatastore { + Param( + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + Get-SrmProtectionGroup -SrmServer $SrmServer -Type "san" | %{ + $pg = $_ + $pginfo = $pg.GetInfo() + $pds = Get-SrmProtectedDatastore -ProtectionGroup $pg + $pds | %{ + $pd = $_ + $output = "" | select datacenter, group, name, capacity, free + $output.datacenter = $pd.Datacenter.Name + $output.group = $pginfo.Name + $output.name = $pd.Name + $output.capacity = $pd.CapacityGB + $output.free = $pd.FreeSpaceGB + + $output + + } + } | Format-Table -Wrap -AutoSize -GroupBy "datacenter" @{Label="Datastore Name"; Expression={$_.name} }, + @{Label="Capacity GB"; Expression={$_.capacity} }, + @{Label="Free GB"; Expression={$_.free} }, + @{Label="Protection Group"; Expression={$_.group} } +} + + +Function Get-SrmConfigReportProtectedVm { + Param( + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + $srmversion = Get-SrmServerVersion -SrmServer $SrmServer + $srmMajorVersion, $srmMinorVersion = $srmversion -split "\." + + Get-SrmProtectionGroup -SrmServer $SrmServer | %{ + $pg = $_ + $pginfo = $pg.GetInfo() + $pvms = Get-SrmProtectedVM -ProtectionGroup $pg + $rps = Get-SrmRecoveryPlan -ProtectionGroup $pg + $rpnames = $rps | %{ $_.GetInfo().Name } + $pvms | %{ + $pvm = $_ + if ($srmMajorVersion -ge 6 -or ($srmMajorVersion -eq 5 -and $srmMinorVersion -eq 8)) { + $rs = $rps | Select -First 1 | %{ $_.GetRecoverySettings($pvm.Vm.MoRef) } + } + $output = "" | select group, name, moRef, needsConfiguration, state, plans, priority, finalPowerState, preCallouts, postCallouts + $output.group = $pginfo.Name + $output.name = $pvm.Vm.Name + $output.moRef = $pvm.Vm.MoRef # this is necessary in case we can't retrieve the name when VC is unavailable + $output.needsConfiguration = $pvm.NeedsConfiguration + $output.state = $pvm.State + $output.plans = [string]::Join(",`r`n", $rpnames) + if ($rs) { + $output.priority = $rs.RecoveryPriority + $output.finalPowerState = $rs.FinalPowerState + $output.preCallouts = $rs.PrePowerOnCallouts.Count + $output.postCallouts = $rs.PostPowerOnCallouts.Count + } + $output + + } + } | Format-Table -Wrap -AutoSize @{Label="VM Name"; Expression={$_.name} }, + @{Label="VM MoRef"; Expression={$_.moRef} }, + @{Label="Needs Config"; Expression={$_.needsConfiguration} }, + @{Label="VM Protection State"; Expression={$_.state} }, + @{Label="Protection Group"; Expression={$_.group} }, + @{Label="Recovery Plans"; Expression={$_.plans} }, + @{Label="Recovery Priority"; Expression={$_.priority} }, + @{Label="Final Power State"; Expression={$_.finalPowerState} }, + @{Label="Pre-PowerOn Callouts"; Expression={$_.preCallouts} }, + @{Label="Post-PowerOn Callouts"; Expression={$_.postCallouts} } + +} + +Function Get-SrmConfigReport { + Param( + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + Get-SrmConfigReportSite -SrmServer $SrmServer + Get-SrmConfigReportPlan -SrmServer $SrmServer + Get-SrmConfigReportProtectionGroup -SrmServer $SrmServer + Get-SrmConfigReportProtectedDatastore -SrmServer $SrmServer + Get-SrmConfigReportProtectedVm -SrmServer $SrmServer +} diff --git a/Modules/SRM/Examples/SrmTagging.ps1 b/Modules/SRM/Examples/SrmTagging.ps1 new file mode 100644 index 0000000..1e33659 --- /dev/null +++ b/Modules/SRM/Examples/SrmTagging.ps1 @@ -0,0 +1,34 @@ +# Depends on SRM Helper Methods - https://github.com/benmeadowcroft/SRM-Cmdlets +# It is assumed that the connections to active VC and SRM Server have already been made + +Import-Module Meadowcroft.SRM -Prefix Srm + +$TagCategoryName = 'Meadowcroft.SRM.VM' +$TagCategoryDescription = 'Tag category for tagging VMs with SRM state' + +# If the tag category doesn't exist, create it and the relevant tags +$TagCategory = Get-TagCategory -Name $TagCategoryName -ErrorAction SilentlyContinue +if (-Not $TagCategory) { + Write-Output "Creating Tag Category $TagCategoryName" + $TagCategory = New-TagCategory -Name $TagCategoryName -Description $TagCategoryDescription -EntityType 'VirtualMachine' + + Write-Output "Creating Tag SrmProtectedVm" + New-Tag -Name 'SrmProtectedVm' -Category $TagCategory -Description "VM protected by VMware SRM" + Write-Output "Creating Tag SrmTestVm" + New-Tag -Name 'SrmTestVm' -Category $TagCategory -Description "Test VM instantiated by VMware SRM" + Write-Output "Creating Tag SrmPlaceholderVm" + New-Tag -Name 'SrmPlaceholderVm' -Category $TagCategory -Description "Placeholder VM used by VMware SRM" +} + +$protectedVmTag = Get-Tag -Name 'SrmProtectedVm' -Category $TagCategory +$testVmTag = Get-Tag -Name 'SrmTestVm' -Category $TagCategory +$placeholderVmTag = Get-Tag -Name 'SrmPlaceholderVm' -Category $TagCategory + +# Assign protected tag to a VM, use ready state to get "local" protected VMs +Get-SrmProtectedVM -State Ready | %{ New-TagAssignment -Tag $protectedVmTag -Entity $(Get-VIObjectByVIView $_.Vm) | Out-Null } + +# Assign test tag to a VM +Get-SrmTestVM | %{ New-TagAssignment -Tag $testVmTag -Entity $_ | Out-Null } + +# Assign placeholder tag to a VM +Get-SrmPlaceholderVM | %{ New-TagAssignment -Tag $placeholderVmTag -Entity $_ | Out-Null } diff --git a/Modules/SRM/LICENSE.txt b/Modules/SRM/LICENSE.txt new file mode 100644 index 0000000..22896f0 --- /dev/null +++ b/Modules/SRM/LICENSE.txt @@ -0,0 +1,74 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and +(b) You must cause any modified files to carry prominent notices stating that You changed the files; and +(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied. + + See the License for the specific language governing permissions and + limitations under the License. + + diff --git a/Modules/SRM/Meadowcroft.Srm.Protection.ps1 b/Modules/SRM/Meadowcroft.Srm.Protection.ps1 new file mode 100644 index 0000000..d522cb6 --- /dev/null +++ b/Modules/SRM/Meadowcroft.Srm.Protection.ps1 @@ -0,0 +1,422 @@ +# SRM Helper Methods - https://github.com/benmeadowcroft/SRM-Cmdlets + +<# +.SYNOPSIS +Get the subset of protection groups matching the input criteria + +.PARAMETER Name +Return protection groups matching the specified name + +.PARAMETER Type +Return protection groups matching the specified protection group +type. For SRM 5.0-5.5 this is either 'san' for protection groups +consisting of a set of replicated datastores or 'vr' for vSphere +Replication based protection groups. + +.PARAMETER RecoveryPlan +Return protection groups associated with a particular recovery +plan + +.PARAMETER SrmServer +the SRM server to use for this operation. +#> +Function Get-ProtectionGroup { + [cmdletbinding()] + Param( + [Parameter(position=1)][string] $Name, + [string] $Type, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan[]] $RecoveryPlan, + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + begin { + $api = Get-ServerApiEndpoint -SrmServer $SrmServer + $pgs = @() + } + process { + if ($RecoveryPlan) { + foreach ($rp in $RecoveryPlan) { + $pgs += $RecoveryPlan.GetInfo().ProtectionGroups + } + $pgs = Select_UniqueByMoRef($pgs) + } else { + $pgs += $api.Protection.ListProtectionGroups() + } + } + end { + $pgs | ForEach-Object { + $pg = $_ + $pgi = $pg.GetInfo() + $selected = (-not $Name -or ($Name -eq $pgi.Name)) -and (-not $Type -or ($Type -eq $pgi.Type)) + if ($selected) { + Add-Member -InputObject $pg -MemberType NoteProperty -Name "Name" -Value $pgi.Name + $pg + } + } + } +} + +<# +.SYNOPSIS +Get the subset of protected VMs matching the input criteria + +.PARAMETER Name +Return protected VMs matching the specified name + +.PARAMETER State +Return protected VMs matching the specified state. For protected +VMs on the protected site this is usually 'ready', for +placeholder VMs this is 'shadowing' + +.PARAMETER ProtectionGroup +Return protected VMs associated with particular protection +groups +#> +Function Get-ProtectedVM { + [cmdletbinding()] + Param( + [Parameter(position=1)][string] $Name, + [VMware.VimAutomation.Srm.Views.SrmProtectionGroupProtectionState] $State, + [VMware.VimAutomation.Srm.Views.SrmProtectionGroupProtectionState] $PeerState, + [switch] $ConfiguredOnly, + [switch] $UnconfiguredOnly, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroup[]] $ProtectionGroup, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan[]] $RecoveryPlan, + [string] $ProtectionGroupName, + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + if ($null -eq $ProtectionGroup) { + $ProtectionGroup = Get-ProtectionGroup -Name $ProtectionGroupName -RecoveryPlan $RecoveryPlan -SrmServer $SrmServer + } + $ProtectionGroup | ForEach-Object { + $pg = $_ + $pg.ListProtectedVms() | ForEach-Object { + # try and update the view data for the protected VM + try { + $_.Vm.UpdateViewData() + } catch { + Write-Error $_ + } finally { + $_ + } + } | Where-object { -not $Name -or ($Name -eq $_.Vm.Name) } | + where-object { -not $State -or ($State -eq $_.State) } | + where-object { -not $PeerState -or ($PeerState -eq $_.PeerState) } | + where-object { ($ConfiguredOnly -and $_.NeedsConfiguration -eq $false) -or ($UnconfiguredOnly -and $_.NeedsConfiguration -eq $true) -or (-not $ConfiguredOnly -and -not $UnconfiguredOnly) } + } +} + + +<# +.SYNOPSIS +Get the unprotected VMs that are associated with a protection group + +.PARAMETER ProtectionGroup +Return unprotected VMs associated with particular protection +groups. For VR protection groups this is VMs that are associated +with the PG but not configured, For ABR protection groups this is +VMs on replicated datastores associated with the group that are not +configured. +#> +Function Get-UnProtectedVM { + [cmdletbinding()] + Param( + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroup[]] $ProtectionGroup, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan[]] $RecoveryPlan, + [string] $ProtectionGroupName, + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + if ($null -eq $ProtectionGroup) { + $ProtectionGroup = Get-ProtectionGroup -Name $ProtectionGroupName -RecoveryPlan $RecoveryPlan -SrmServer $SrmServer + } + + $associatedVMs = @() + $protectedVmRefs = @() + + $ProtectionGroup | ForEach-Object { + $pg = $_ + # For VR listAssociatedVms to get list of VMs + if ($pg.GetInfo().Type -eq 'vr') { + $associatedVMs += @($pg.ListAssociatedVms() | Get-VIObjectByVIView) + } + # TODO test this: For ABR get VMs on GetProtectedDatastore + if ($pg.GetInfo().Type -eq 'san') { + $pds = @(Get-ProtectedDatastore -ProtectionGroup $pg) + $pds | ForEach-Object { + $ds = Get-Datastore -id $_.MoRef + $associatedVMs += @(Get-VM -Datastore $ds) + } + } + + # get protected VMs + $protectedVmRefs += @(Get-ProtectedVM -ProtectionGroup $pg | ForEach-Object { $_.Vm.MoRef } | Select-Object -Unique) + } + + # get associated but unprotected VMs + $associatedVMs | Where-Object { $protectedVmRefs -notcontains $_.ExtensionData.MoRef } +} + + +#Untested as I don't have ABR setup in my lab yet +<# +.SYNOPSIS +Get the subset of protected Datastores matching the input criteria + +.PARAMETER ProtectionGroup +Return protected datastores associated with particular protection +groups +#> +Function Get-ProtectedDatastore { + [cmdletbinding()] + Param( + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroup[]] $ProtectionGroup, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan[]] $RecoveryPlan, + [string] $ProtectionGroupName, + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + if (-not $ProtectionGroup) { + $ProtectionGroup = Get-ProtectionGroup -Name $ProtectionGroupName -RecoveryPlan $RecoveryPlan -SrmServer $SrmServer + } + $ProtectionGroup | ForEach-Object { + $pg = $_ + if ($pg.GetInfo().Type -eq 'san') { # only supported for array based replication datastores + $pg.ListProtectedDatastores() + } + } +} + + +#Untested as I don't have ABR setup in my lab yet +<# +.SYNOPSIS +Get the replicated datastores that aren't associated with a protection group. +#> +Function Get-ReplicatedDatastore { + [cmdletbinding()] + Param( + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + $api = Get-ServerApiEndpoint -SrmServer $SrmServer + + $api.Protection.ListUnassignedReplicatedDatastores() +} + +<# +.SYNOPSIS +Protect a VM using SRM + +.PARAMETER ProtectionGroup +The protection group that this VM will belong to + +.PARAMETER Vm +The virtual machine to protect +#> +Function Protect-VM { + [cmdletbinding()] + Param( + [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroup] $ProtectionGroup, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $Vm, + [Parameter (ValueFromPipeline=$true)][VMware.Vim.VirtualMachine] $VmView + ) + + $moRef = Get_MoRefFromVmObj -Vm $Vm -VmView $VmView + + $pgi = $ProtectionGroup.GetInfo() + #TODO query protection status first + + if ($moRef) { + if ($pgi.Type -eq 'vr') { + $ProtectionGroup.AssociateVms(@($moRef)) + } + $protectionSpec = New-Object VMware.VimAutomation.Srm.Views.SrmProtectionGroupVmProtectionSpec + $protectionSpec.Vm = $moRef + $protectTask = $ProtectionGroup.ProtectVms($protectionSpec) + while(-not $protectTask.IsComplete()) { Start-Sleep -Seconds 1 } + $protectTask.GetResult() + } else { + throw "Can't protect the VM, no MoRef found." + } +} + + +<# +.SYNOPSIS +Unprotect a VM using SRM + +.PARAMETER ProtectionGroup +The protection group that this VM will be removed from + +.PARAMETER Vm +The virtual machine to unprotect +#> +Function Unprotect-VM { + [cmdletbinding()] + Param( + [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroup] $ProtectionGroup, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $Vm, + [Parameter (ValueFromPipeline=$true)][VMware.Vim.VirtualMachine] $VmView, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroupProtectedVm] $ProtectedVm + ) + + $moRef = Get_MoRefFromVmObj -Vm $Vm -VmView $VmView -ProtectedVm $ProtectedVm + + $pgi = $ProtectionGroup.GetInfo() + $protectTask = $ProtectionGroup.UnprotectVms($moRef) + while(-not $protectTask.IsComplete()) { Start-Sleep -Seconds 1 } + if ($pgi.Type -eq 'vr') { + $ProtectionGroup.UnassociateVms(@($moRef)) + } + $protectTask.GetResult() +} + +<# +.SYNOPSIS +Get a protection group folder + +.PARAMETER SrmServer +The SRM Server to query for the protection group folder +#> +Function Get-ProtectionGroupFolder { + [cmdletbinding()] + Param( + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + $api = Get-ServerApiEndpoint -SrmServer $SrmServer + + $folder = $api.Protection.GetProtectionGroupRootFolder() + + return $folder +} + +<# +.SYNOPSIS +Create a new protection group + +.PARAMETER Name +The name of the protection group + +.PARAMETER Description +Description of the protection group + +.PARAMETER Folder +The protection group folder in which to create the new protection group + +.PARAMETER ArrayReplication +Set if protection group is for replicating VMs using Array based replication + +.PARAMETER vSphereReplication +Set if protection group is for replicating VMs with vSphere Replication + +.PARAMETER VMs +For vSphere Replication based protection, the VMs to add to the replication +group. These should already be replicated. + +.PARAMETER VMViews +For vSphere Replication based protection, the VMs to add to the replication +group. These should already be replicated. + +.PARAMETER SrmServer +The SRM Server to perform the operation against +#> +Function New-ProtectionGroup { + [cmdletbinding(DefaultParameterSetName="VR", SupportsShouldProcess=$True, ConfirmImpact="Medium")] + [OutputType([VMware.VimAutomation.Srm.Views.SrmProtectionGroup])] + Param( + [Parameter (Mandatory=$true)] $Name, + $Description, + [VMware.VimAutomation.Srm.Views.SrmProtectionGroupFolder] $Folder, + [Parameter (ParameterSetName="ABR", Mandatory=$true)][switch] $ArrayReplication, + [Parameter (ValueFromPipeline=$true, ParameterSetName="ABR")][VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore[]] $Datastores, + [Parameter (ValueFromPipeline=$true, ParameterSetName="ABR")][VMware.Vim.Datastore[]] $DatastoreViews, + [Parameter (ParameterSetName="VR", Mandatory=$true)][switch] $vSphereReplication, + [Parameter (ValueFromPipeline=$true, ParameterSetName="VR")][VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]] $VMs, + [Parameter (ValueFromPipeline=$true, ParameterSetName="VR")][VMware.Vim.VirtualMachine[]] $VMViews, + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + $api = Get-ServerApiEndpoint $SrmServer + [VMware.VimAutomation.Srm.Views.SrmCreateProtectionGroupTask] $task = $null + + #get root folder if this wasn't specified as a parameter + if(-not $Folder) { + $Folder = Get-ProtectionGroupFolder -SrmServer $SrmServer + } + + if ($vSphereReplication) { + #create list of managed object references from VM and/or VM view arrays + [VMware.Vim.ManagedObjectReference[]]$moRefs = @() + foreach ($vm in $VMs) { + $moRefs += Get_MoRefFromVmObj -Vm $Vm + } + foreach ($VmView in $VMViews) { + $moRefs += Get_MoRefFromVmObj -VmView $VmView + } + + if ($pscmdlet.ShouldProcess($Name, "New")) { + $task = $api.Protection.CreateHbrProtectionGroup($Folder.MoRef, $Name, $Description, $moRefs) + } + + } elseif ($ArrayReplication) { + #create list of managed object references from VM and/or VM view arrays + $moRefs = @() + foreach ($ds in $Datastores) { + $moRefs += $ds.ExtensionData.MoRef + } + foreach ($DsView in $DatastoreViews) { + $moRefs += $DsView.MoRef + } + + if ($pscmdlet.ShouldProcess($Name, "New")) { + $task = $api.Protection.CreateAbrProtectionGroup($Folder.MoRef, $Name, $Description, $moRefs) + } + + } else { + throw "Undetermined protection group type" + } + + # Complete task + while(-not $task.IsCreateProtectionGroupComplete()) { Start-Sleep -Seconds 1 } + + # Retrieve the protection group, and protect associated VMs + $pg = $task.GetNewProtectionGroup() + if ($pg) { + $unProtectedVMs = Get-UnProtectedVM -ProtectionGroup $pg + $unProtectedVMs | Protect-VM -ProtectionGroup $pg + } + + return $pg +} + + +<# +.SYNOPSIS +Delete a protection group + +.PARAMETER ProtectionGroup +The protection group to remove + +.PARAMETER SrmServer +The SRM Server to perform the operation against +#> +Function Remove-ProtectionGroup { + [cmdletbinding(SupportsShouldProcess=$True, ConfirmImpact="High")] + [OutputType([VMware.VimAutomation.Srm.Views.RemoveProtectionGroupTask])] + Param( + [Parameter (Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroup] $ProtectionGroup, + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + $api = Get-ServerApiEndpoint $SrmServer + [VMware.VimAutomation.Srm.Views.RemoveProtectionGroupTask] $task = $null + + $pginfo = $ProtectionGroup.GetInfo() + if ($pscmdlet.ShouldProcess($pginfo.Name, "Remove")) { + $task = $api.Protection.RemoveProtectionGroup($ProtectionGroup.MoRef) + } + + return $task +} diff --git a/Modules/SRM/Meadowcroft.Srm.Recovery.ps1 b/Modules/SRM/Meadowcroft.Srm.Recovery.ps1 new file mode 100644 index 0000000..1b2eaca --- /dev/null +++ b/Modules/SRM/Meadowcroft.Srm.Recovery.ps1 @@ -0,0 +1,556 @@ +# SRM Helper Methods - https://github.com/benmeadowcroft/SRM-Cmdlets + +<# +.SYNOPSIS +Get the subset of recovery plans matching the input criteria + +.PARAMETER Name +Return recovery plans matching the specified name + +.PARAMETER ProtectionGroup +Return recovery plans associated with particular protection +groups +#> +Function Get-RecoveryPlan { + [cmdletbinding()] + Param( + [Parameter(position=1)][string] $Name, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroup[]] $ProtectionGroup, + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + begin { + $api = Get-ServerApiEndpoint -SrmServer $SrmServer + $rps = @() + } + process { + if ($ProtectionGroup) { + foreach ($pg in $ProtectionGroup) { + $rps += $pg.ListRecoveryPlans() + } + $rps = Select_UniqueByMoRef($rps) + } else { + $rps += $api.Recovery.ListPlans() + } + } + end { + $rps | ForEach-Object { + $rp = $_ + $rpi = $rp.GetInfo() + $selected = (-not $Name -or ($Name -eq $rpi.Name)) + if ($selected) { + Add-Member -InputObject $rp -MemberType NoteProperty -Name "Name" -Value $rpi.Name + $rp + } + } + } +} + +<# +.SYNOPSIS +Start a Recovery Plan action like test, recovery, cleanup, etc. + +.PARAMETER RecoveryPlan +The recovery plan to start + +.PARAMETER RecoveryMode +The recovery mode to invoke on the plan. May be one of "Test", "Cleanup", "Failover", "Migrate", "Reprotect" +#> +Function Start-RecoveryPlan { + [cmdletbinding(SupportsShouldProcess=$True, ConfirmImpact="High")] + Param( + [Parameter (Mandatory=$true, ValueFromPipeline=$true, Position=1)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, + [VMware.VimAutomation.Srm.Views.SrmRecoveryPlanRecoveryMode] $RecoveryMode = [VMware.VimAutomation.Srm.Views.SrmRecoveryPlanRecoveryMode]::Test, + [bool] $SyncData = $True + ) + + # Validate with informative error messages + $rpinfo = $RecoveryPlan.GetInfo() + + # Create recovery options + $rpOpt = New-Object VMware.VimAutomation.Srm.Views.SrmRecoveryOptions + $rpOpt.SyncData = $SyncData + + # Prompt the user to confirm they want to execute the action + if ($pscmdlet.ShouldProcess($rpinfo.Name, $RecoveryMode)) { + if ($rpinfo.State -eq 'Protecting') { + throw "This recovery plan action needs to be initiated from the other SRM instance" + } + + $RecoveryPlan.Start($RecoveryMode, $rpOpt) + } +} + +<# +.SYNOPSIS +Stop a running Recovery Plan action. + +.PARAMETER RecoveryPlan +The recovery plan to stop +#> +Function Stop-RecoveryPlan { + [cmdletbinding(SupportsShouldProcess=$True,ConfirmImpact="High")] + Param( + [Parameter (Mandatory=$true, ValueFromPipeline=$true, Position=1)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan + ) + + # Validate with informative error messages + $rpinfo = $RecoveryPlan.GetInfo() + + # Prompt the user to confirm they want to cancel the running action + if ($pscmdlet.ShouldProcess($rpinfo.Name, 'Cancel')) { + + $RecoveryPlan.Cancel() + } +} + +<# +.SYNOPSIS +Retrieve the historical results of a recovery plan + +.PARAMETER RecoveryPlan +The recovery plan to retrieve the history for +#> +Function Get-RecoveryPlanResult { + [cmdletbinding()] + Param( + [Parameter (Mandatory=$true, ValueFromPipeline=$true, Position=1)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, + [VMware.VimAutomation.Srm.Views.SrmRecoveryPlanRecoveryMode] $RecoveryMode, + [VMware.VimAutomation.Srm.Views.SrmRecoveryResultResultState] $ResultState, + [DateTime] $StartedAfter, + [DateTime] $startedBefore, + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + $api = Get-ServerApiEndpoint -SrmServer $SrmServer + + # Get the history objects + $history = $api.Recovery.GetHistory($RecoveryPlan.MoRef) + $resultCount = $history.GetResultCount() + + if ($resultCount -gt 0) { + $results = $history.GetRecoveryResult($resultCount) + + $results | + Where-Object { -not $RecoveryMode -or $_.RunMode -eq $RecoveryMode } | + Where-Object { -not $ResultState -or $_.ResultState -eq $ResultState } | + Where-Object { $null -eq $StartedAfter -or $_.StartTime -gt $StartedAfter } | + Where-Object { $null -eq $StartedBefore -or $_.StartTime -lt $StartedBefore } + } +} + +<# +.SYNOPSIS +Exports a recovery plan result object to XML format + +.PARAMETER RecoveryPlanResult +The recovery plan result to export +#> +Function Export-RecoveryPlanResultAsXml { + [cmdletbinding()] + [OutputType([xml])] + Param( + [Parameter (Mandatory=$true, ValueFromPipeline=$true, Position=1)][VMware.VimAutomation.Srm.Views.SrmRecoveryResult] $RecoveryPlanResult, + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + $api = Get-ServerApiEndpoint -SrmServer $SrmServer + + $RecoveryPlan = $RecoveryPlanResult.Plan + $history = $api.Recovery.GetHistory($RecoveryPlan.MoRef) + $lines = $history.GetResultLength($RecoveryPlanResult.Key) + [xml] $history.RetrieveStatus($RecoveryPlanResult.Key, 0, $lines) +} + +<# +.SYNOPSIS +Add a protection group to a recovery plan. This requires SRM 5.8 or later. + +.PARAMETER RecoveryPlan +The recovery plan the protection group will be associated with + +.PARAMETER ProtectionGroup +The protection group to associate with the recovery plan +#> +Function Add-ProtectionGroupToRecoveryPlan { + [cmdletbinding()] + Param( + [Parameter (Mandatory=$true, Position=1)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, + [Parameter (Mandatory=$true, ValueFromPipeline=$true, Position=2)][VMware.VimAutomation.Srm.Views.SrmProtectionGroup] $ProtectionGroup + ) + + if ($RecoveryPlan -and $ProtectionGroup) { + foreach ($pg in $ProtectionGroup) { + try { + $RecoveryPlan.AddProtectionGroup($pg.MoRef) + } catch { + Write-Error $_ + } + } + } +} + +<# +.SYNOPSIS +Remove a protection group to a recovery plan. This requires SRM 6.5 or later. + +.PARAMETER RecoveryPlan +The recovery plan the protection group will be disassociated from + +.PARAMETER ProtectionGroup +The protection group to disassociate from the recovery plan +#> +Function Remove-ProtectionGroupFromRecoveryPlan { + [cmdletbinding()] + Param( + [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, + [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroup] $ProtectionGroup + ) + + if ($RecoveryPlan -and $ProtectionGroup) { + foreach ($pg in $ProtectionGroup) { + try { + $RecoveryPlan.RemoveProtectionGroupFromRecoveryPlan($pg.MoRef) + } catch { + Write-Error $_ + } + } + } +} + +<# +.SYNOPSIS +Get the recovery settings of a protected VM. This requires SRM 5.8 or later. + +.PARAMETER RecoveryPlan +The recovery plan the settings will be retrieved from. + +.PARAMETER Vm +The virtual machine to retieve recovery settings for. + +#> +Function Get-RecoverySetting { + [cmdletbinding()] + Param( + [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $Vm, + [Parameter (ValueFromPipeline=$true)][VMware.Vim.VirtualMachine] $VmView, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroupProtectedVm] $ProtectedVm + ) + + $moRef = Get_MoRefFromVmObj -Vm $Vm -VmView $VmView -ProtectedVm $ProtectedVm + + if ($RecoveryPlan -and $moRef) { + $RecoveryPlan.GetRecoverySettings($moRef) + } +} + +<# +.SYNOPSIS +Get the recovery settings of a protected VM. This requires SRM 5.8 or later. + +.PARAMETER RecoveryPlan +The recovery plan the settings will be retrieved from. + +.PARAMETER Vm +The virtual machine to configure recovery settings on. + +.PARAMETER RecoverySettings +The recovery settings to configure. These should have been retrieved via a +call to Get-RecoverySettings + +#> +Function Set-RecoverySetting { + [cmdletbinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] + Param( + [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $Vm, + [Parameter (ValueFromPipeline=$true)][VMware.Vim.VirtualMachine] $VmView, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroupProtectedVm] $ProtectedVm, + [Parameter (Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoverySettings] $RecoverySettings + ) + + + $moRef = Get_MoRefFromVmObj -Vm $Vm -VmView $VmView -ProtectedVm $ProtectedVm + + if ($RecoveryPlan -and $moRef -and $RecoverySettings) { + if ($PSCmdlet.ShouldProcess("$moRef", "Set")) { + $RecoveryPlan.SetRecoverySettings($moRef, $RecoverySettings) + } + } +} + +<# +.SYNOPSIS +Create a new per-Vm command to add to the SRM Recovery Plan + +.PARAMETER Command +The command script to execute. + +.PARAMETER Description +The user friendly description of this script. + +.PARAMETER Timeout +The number of seconds this command has to execute before it will be timedout. + +.PARAMETER RunInRecoveredVm +For a post-power on command this flag determines whether it will run on the +recovered VM or on the SRM server. + +#> +Function New-Command { + [cmdletbinding(SupportsShouldProcess=$true, ConfirmImpact="None")] + Param( + [Parameter (Mandatory=$true)][string] $Command, + [Parameter (Mandatory=$true)][string] $Description, + [int] $Timeout = 300, + [switch] $RunInRecoveredVm = $false + ) + + if($PSCmdlet.ShouldProcess("Description", "New")) { + $srmWsdlCmd = New-Object VMware.VimAutomation.Srm.WsdlTypes.SrmCommand + $srmCmd = New-Object VMware.VimAutomation.Srm.Views.SrmCommand -ArgumentList $srmWsdlCmd + $srmCmd.Command = $Command + $srmCmd.Description = $Description + $srmCmd.RunInRecoveredVm = $RunInRecoveredVm + $srmCmd.Timeout = $Timeout + $srmCmd.Uuid = [guid]::NewGuid() + + return $srmCmd + } +} + +<# Internal function #> +Function Add_Command { + [cmdletbinding()] + Param( + [Parameter (Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoverySettings] $RecoverySettings, + [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmCommand] $SrmCommand, + [Parameter (Mandatory=$true)][bool] $PostRecovery + ) + + if ($PostRecovery) { + $commands = $RecoverySettings.PostPowerOnCallouts + } else { + $commands = $RecoverySettings.PrePowerOnCallouts + } + + if (-not $commands) { + $commands = New-Object System.Collections.Generic.List[VMware.VimAutomation.Srm.Views.SrmCallout] + } + $commands.Add($SrmCommand) + + if ($PostRecovery) { + $RecoverySettings.PostPowerOnCallouts = $commands + } else { + $RecoverySettings.PrePowerOnCallouts = $commands + } +} + +<# +.SYNOPSIS +Add an SRM command to the set of pre recovery callouts for a VM. + +.PARAMETER RecoverySettings +The recovery settings to update. These should have been retrieved via a +call to Get-RecoverySettings + +.PARAMETER SrmCommand +The command to add to the list. + +#> +Function Add-PreRecoveryCommand { + [cmdletbinding()] + [OutputType([VMware.VimAutomation.Srm.Views.SrmRecoverySettings])] + Param( + [Parameter (Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoverySettings] $RecoverySettings, + [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmCommand] $SrmCommand + ) + Add_Command -RecoverySettings $RecoverySettings -SrmCommand $SrmCommand -PostRecovery $false + return $RecoverySettings +} + +<# +.SYNOPSIS +Remove an SRM command from the set of pre recovery callouts for a VM. + +.PARAMETER RecoverySettings +The recovery settings to update. These should have been retrieved via a +call to Get-RecoverySettings + +.PARAMETER SrmCommand +The command to remove from the list. + +#> +Function Remove-PreRecoveryCommand { + [cmdletbinding(SupportsShouldProcess=$true, ConfirmImpact="Low")] + [OutputType([VMware.VimAutomation.Srm.Views.SrmRecoverySettings])] + Param( + [Parameter (Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoverySettings] $RecoverySettings, + [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmCommand] $SrmCommand + ) + + if ($pscmdlet.ShouldProcess($SrmCommand.Description, "Remove")) { + $RecoverySettings.PrePowerOnCallouts.Remove($SrmCommand) + } + + return $RecoverySettings +} + +<# +.SYNOPSIS +Add an SRM command to the set of post recovery callouts for a VM. + +.PARAMETER RecoverySettings +The recovery settings to update. These should have been retrieved via a +call to Get-RecoverySettings + +.PARAMETER SrmCommand +The command to add to the list. + +#> +Function Add-PostRecoveryCommand { + [cmdletbinding()] + [OutputType([VMware.VimAutomation.Srm.Views.SrmRecoverySettings])] + Param( + [Parameter (Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoverySettings] $RecoverySettings, + [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmCommand] $SrmCommand + ) + + Add_Command -RecoverySettings $RecoverySettings -SrmCommand $SrmCommand -PostRecovery $true + + return $RecoverySettings +} + + +<# +.SYNOPSIS +Remove an SRM command from the set of post recovery callouts for a VM. + +.PARAMETER RecoverySettings +The recovery settings to update. These should have been retrieved via a +call to Get-RecoverySettings + +.PARAMETER SrmCommand +The command to remove from the list. + +#> +Function Remove-PostRecoveryCommand { + [cmdletbinding(SupportsShouldProcess=$true, ConfirmImpact="Low")] + [OutputType([VMware.VimAutomation.Srm.Views.SrmRecoverySettings])] + Param( + [Parameter (Mandatory=$true, ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmRecoverySettings] $RecoverySettings, + [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmCommand] $SrmCommand + ) + + if ($pscmdlet.ShouldProcess($SrmCommand.Description, "Remove")) { + $RecoverySettings.PostPowerOnCallouts.Remove($SrmCommand) + } + + return $RecoverySettings +} + + +<# +.SYNOPSIS +Create a new recovery plan + +.PARAMETER Name +The name for this recovery plan + +.PARAMETER Description +A description of the recovery plan + +.PARAMETER Folder +The recovery plan folder in which to create this recovery plan. Will default to +the root recovery plan folder + +.PARAMETER ProtectionGroups +The protection groups to associate with this recovery plan + +.PARAMETER TestNetworkMappings +The test network mappings to configure as part of this recovery plan + +.PARAMETER SrmServer +The SRM Server to operate against +#> +Function New-RecoveryPlan { + [cmdletbinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] + Param( + [Parameter (Mandatory=$true)][string] $Name, + [string] $Description, + [VMware.VimAutomation.Srm.Views.SrmRecoveryPlanFolder] $Folder, + [VMware.VimAutomation.Srm.Views.SrmProtectionGroup[]] $ProtectionGroups, + [VMware.VimAutomation.Srm.Views.SrmRecoveryTestNetworkMapping[]] $TestNetworkMappings, + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + $api = Get-ServerApiEndpoint -SrmServer $SrmServer + + if (-not $Folder) { + $Folder = Get-RecoveryPlanFolder -SrmServer $SrmServer + } + + $protectionGroupmRefs += @( $ProtectionGroups | ForEach-Object { $_.MoRef } | Select-Object -Unique) + + [VMware.VimAutomation.Srm.Views.CreateRecoveryPlanTask] $task = $null + + if ($PSCmdlet.ShouldProcess($Name, "New")) { + $task = $api.Recovery.CreateRecoveryPlan( + $Name, + $Folder.MoRef, + $protectionGroupmRefs, + $Description, + $TestNetworkMappings + ) + } + + while(-not $task.IsCreateRecoveryPlanComplete()) { Start-Sleep -Seconds 1 } + + $task.GetNewRecoveryPlan() +} + +<# +.SYNOPSIS +Remove a recovery plan permanently + +.PARAMETER RecoveryPlan +The recovery plan to remove + +.PARAMETER SrmServer +The SRM Server to operate against +#> +Function Remove-RecoveryPlan { + [cmdletbinding(SupportsShouldProcess=$True, ConfirmImpact="High")] + Param( + [Parameter (Mandatory=$true)][VMware.VimAutomation.Srm.Views.SrmRecoveryPlan] $RecoveryPlan, + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + $api = Get-ServerApiEndpoint -SrmServer $SrmServer + + $rpinfo = $RecoveryPlan.GetInfo() + if ($pscmdlet.ShouldProcess($rpinfo.Name, "Remove")) { + $api.Recovery.DeleteRecoveryPlan($RecoveryPlan.MoRef) + } +} + +<# +.SYNOPSIS +Get a recovery plan folder + +.PARAMETER SrmServer +The SRM Server to query for the recovery plan folder +#> +Function Get-RecoveryPlanFolder { + [cmdletbinding()] + Param( + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + $api = Get-ServerApiEndpoint -SrmServer $SrmServer + + $folder = $api.Recovery.GetRecoveryPlanRootFolder() + + return $folder +} diff --git a/Modules/SRM/Meadowcroft.Srm.Storage.ps1 b/Modules/SRM/Meadowcroft.Srm.Storage.ps1 new file mode 100644 index 0000000..8880f36 --- /dev/null +++ b/Modules/SRM/Meadowcroft.Srm.Storage.ps1 @@ -0,0 +1,24 @@ +# SRM Helper Methods - https://github.com/benmeadowcroft/SRM-Cmdlets + +<# +.SYNOPSIS +Trigger Discover Devices for Site Recovery Manager + +.OUTPUTS +Returns discover devices task +#> +Function Start-DiscoverDevice { + [cmdletbinding(SupportsShouldProcess=$True, ConfirmImpact="Medium")] + [OutputType([VMware.VimAutomation.Srm.Views.DiscoverDevicesTask])] + Param( + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + $api = Get-ServerApiEndpoint -SrmServer $SrmServer + $name = $SrmServer.Name + [VMware.VimAutomation.Srm.Views.DiscoverDevicesTask] $task = $null + if ($pscmdlet.ShouldProcess($name, "Rescan Storage Devices")) { + $task = $api.Storage.DiscoverDevices() + } + return $task +} diff --git a/Modules/SRM/Meadowcroft.Srm.psd1 b/Modules/SRM/Meadowcroft.Srm.psd1 new file mode 100644 index 0000000..996a493 --- /dev/null +++ b/Modules/SRM/Meadowcroft.Srm.psd1 @@ -0,0 +1,92 @@ +# +# Module manifest for module 'Meadowcroft.Srm' +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'Meadowcroft.Srm.psm1' + +# Version number of this module. +ModuleVersion = '0.2' + +# ID used to uniquely identify this module +GUID = 'f9247009-9168-4a21-831b-819f82884ffe' + +# Author of this module +Author = 'Ben Meadowcroft' + +# Company or vendor of this module +CompanyName = 'VMware, Inc' + +# Copyright statement for this module +Copyright = '(c) 2014 - 2017. All rights reserved.' + +# Description of the functionality provided by this module +# Description = '' + +# Minimum version of the Windows PowerShell engine required by this module +# PowerShellVersion = '' + +# Name of the Windows PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the Windows PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module +# DotNetFrameworkVersion = '' + +# Minimum version of the common language runtime (CLR) required by this module +# CLRVersion = '' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +RequiredModules = @{ModuleName='VMware.VimAutomation.Srm'; ModuleVersion='6.5'} + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +NestedModules = 'Meadowcroft.Srm.Recovery.ps1','Meadowcroft.Srm.Protection.ps1','Meadowcroft.Srm.Storage.ps1' +# NestedModules = @() + +# Functions to export from this module, note that internal functions use '_' not '-' as separator +FunctionsToExport = '*-*' + +# Cmdlets to export from this module +CmdletsToExport = '*' + +# Variables to export from this module +VariablesToExport = '*' + +# Aliases to export from this module +AliasesToExport = '*' + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess +# PrivateData = '' + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +DefaultCommandPrefix = 'Srm' + +} diff --git a/Modules/SRM/Meadowcroft.Srm.psm1 b/Modules/SRM/Meadowcroft.Srm.psm1 new file mode 100644 index 0000000..ed7c042 --- /dev/null +++ b/Modules/SRM/Meadowcroft.Srm.psm1 @@ -0,0 +1,148 @@ +# SRM Helper Methods - https://github.com/benmeadowcroft/SRM-Cmdlets + +<# +.SYNOPSIS +This is intended to be an "internal" function only. It filters a +pipelined input of objects and elimiates duplicates as identified +by the MoRef property on the object. + +.LINK +https://github.com/benmeadowcroft/SRM-Cmdlets/ +#> +Function Select_UniqueByMoRef { + + Param( + [Parameter (ValueFromPipeline=$true)] $in + ) + process { + $moref = New-Object System.Collections.ArrayList + $in | Sort-Object | Select-Object MoRef -Unique | ForEach-Object { $moref.Add($_.MoRef) } > $null + $in | ForEach-Object { + if ($_.MoRef -in $moref) { + $moref.Remove($_.MoRef) + $_ #output + } + } + } +} + +<# +.SYNOPSIS +This is intended to be an "internal" function only. It gets the +MoRef property of a VM from either a VM object, a VM view, or the +protected VM object. +#> +Function Get_MoRefFromVmObj { + Param( + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $Vm, + [Parameter (ValueFromPipeline=$true)][VMware.Vim.VirtualMachine] $VmView, + [Parameter (ValueFromPipeline=$true)][VMware.VimAutomation.Srm.Views.SrmProtectionGroupProtectedVm] $ProtectedVm + ) + + + $moRef = $null + if ($Vm.ExtensionData.MoRef) { # VM object + $moRef = $Vm.ExtensionData.MoRef + } elseif ($VmView.MoRef) { # VM view + $moRef = $VmView.MoRef + } elseif ($protectedVm) { + $moRef = $ProtectedVm.Vm.MoRef + } + + $moRef +} + +<# +.SYNOPSIS +Lookup the srm instance for a specific server. +#> +Function Get-Server { + [cmdletbinding()] + Param( + [string] $SrmServerAddress, + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + $found = $null + + if ($SrmServer) { + $found = $SrmServer + } elseif ($SrmServerAddress) { + # search for server address in default servers + $global:DefaultSrmServers | ForEach-Object { + if ($_.Name -ieq $SrmServerAddress) { + $found = $_ + } + } + if (-not $found) { + throw "SRM server $SrmServerAddress not found. Connect-Server must be called first." + } + } + + if (-not $found) { + #default result + $found = $global:DefaultSrmServers[0] + } + + return $found; +} + +<# +.SYNOPSIS +Retrieve the SRM Server Version +#> +Function Get-ServerVersion { + [cmdletbinding()] + Param( + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + $srm = Get-Server $SrmServer + $srm.Version +} + +<# +.SYNOPSIS +Lookup the SRM API endpoint for a specific server. +#> +Function Get-ServerApiEndpoint { + [cmdletbinding()] + Param( + [string] $SrmServerAddress, + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $SrmServer + ) + + [VMware.VimAutomation.Srm.Types.V1.SrmServer] $server = Get-Server -SrmServerAddress $SrmServerAddress -SrmServer $SrmServer + + return $server.ExtensionData +} + +<# +.SYNOPSIS +Get the placeholder VMs that are associated with SRM +#> +Function Get-PlaceholderVM { + [cmdletbinding()] + Param() + Get-VM @Args | Where-Object {$_.ExtensionData.Config.ManagedBy.extensionKey -like "com.vmware.vcDr*" -and $_.ExtensionData.Config.ManagedBy.Type -ieq 'placeholderVm'} +} + +<# +.SYNOPSIS +Get the test VMs that are associated with SRM +#> +Function Get-TestVM { + [cmdletbinding()] + Param() + Get-VM @Args | Where-Object {$_.ExtensionData.Config.ManagedBy.extensionKey -like "com.vmware.vcDr*" -and $_.ExtensionData.Config.ManagedBy.Type -ieq 'testVm'} +} + +<# +.SYNOPSIS +Get the VMs that are replicated using vSphere Replication. These may not be SRM +protected VMs. +#> +Function Get-ReplicatedVM { + [cmdletbinding()] + Param() + Get-VM @Args | Where-Object {($_.ExtensionData.Config.ExtraConfig | Where-Object { $_.Key -eq 'hbr_filter.destination' -and $_.Value } )} +} diff --git a/Modules/SRM/NOTICE.txt b/Modules/SRM/NOTICE.txt new file mode 100644 index 0000000..2eb2d48 --- /dev/null +++ b/Modules/SRM/NOTICE.txt @@ -0,0 +1,7 @@ + +Copyright (c) 2017 VMware, Inc. All Rights Reserved. + +This product is licensed to you under the Apache License version 2.0 (the "License"). You may not use this product except in compliance with the License. + +This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. + diff --git a/Modules/SRM/README.md b/Modules/SRM/README.md new file mode 100644 index 0000000..00d775e --- /dev/null +++ b/Modules/SRM/README.md @@ -0,0 +1,81 @@ +# SRM PowerCLI Cmdlets + +Helper functions for working with VMware SRM 6.5 with PowerCLI 6.5.1 or later. PowerShell 5.0 and above is required. + +This module is provided for illustrative/educational purposes to explain how the PowerCLI access to the SRM public API can be used. + +## Getting Started + +### Getting the SRM cmdlets + +The latest version of the software can be cloned from the git repository: + + git clone https://github.com/benmeadowcroft/SRM-Cmdlets.git + +Or downloaded as a [zip file](https://github.com/benmeadowcroft/SRM-Cmdlets/archive/master.zip). + +Specific releases (compatible with earlier PowerCLI and SRM versions) can be downloaded via the [release page](https://github.com/benmeadowcroft/SRM-Cmdlets/releases). + +### Deploy SRM-Cmdlets module + +After cloning (or downloading and extracting) the PowerShell module, you can import the module into your current PowerShell session by by passing the path to `Meadowcroft.Srm.psd1` to the `Import-Module` cmdlet, e.g.: + + Import-Module -Name .\SRM-Cmdlets\Meadowcroft.Srm.psd1 + +You can also install the module into the PowerShell path so it can be loaded implicitly. See [Microsoft's Installing Modules instructions](http://msdn.microsoft.com/en-us/library/dd878350) for more details on how to do this. + +The module uses the default prefix of `Srm` for the custom functions it defines. This can be overridden when importing the module by setting the value of the `-Prefix` parameter when calling `Import-Module`. + +### Connecting to SRM + +After installing the module the next step is to connect to the SRM server. Details of how to do this are located in the [PowerCLI 6.5.1 User's Guide](http://pubs.vmware.com/vsphere-65/topic/com.vmware.powercli.ug.doc/GUID-A5F206CF-264D-4565-8CB9-4ED1C337053F.html) + + $credential = Get-Credential + Connect-VIServer -Server vc-a.example.com -Credential $credential + Connect-SrmServer -Credential $credential -RemoteCredential $credential + +At this point we've just been using the cmdlets provided by PowerCLI, the PowerCLI documentation also provides some examples of how to call the SRM API to perform various tasks. In the rest of this introduction we'll perform some of those tasks using the custom functions defined in this project. + +### Report the Protected Virtual Machines and Their Protection Groups + +Goal: Create a simple report listing the VMs protected by SRM and the protection group they belong to. + + Get-SrmProtectionGroup | %{ + $pg = $_ + Get-SrmProtectedVM -ProtectionGroup $pg } | %{ + $output = "" | select VmName, PgName + $output.VmName = $_.Vm.Name + $output.PgName = $pg.GetInfo().Name + $output + } | Format-Table @{Label="VM Name"; Expression={$_.VmName} }, + @{Label="Protection group name"; Expression={$_.PgName} + } + +### Report the Last Recovery Plan Test + +Goal: Create a simple report listing the state of the last test of a recovery plan + + Get-SrmRecoveryPlan | %{ $_ | + Get-SrmRecoveryPlanResult -RecoveryMode Test | select -First 1 + } | Select Name, StartTime, RunMode, ResultState | Format-Table + + +### Execute a Recovery Plan Test + +Goal: for a specific recovery plan, execute a test failover. Note the "local" SRM server we are connected to should be the recovery site in order for this to be successful. + + Get-SrmRecoveryPlan -Name "Name of Plan" | Start-SrmRecoveryPlan -RecoveryMode Test + +### Export the Detailed XML Report of the Last Recovery Plan Workflow + +Goal: get the XML report of the last recovery plan execution for a specific recovery plan. + + Get-SrmRecoveryPlan -Name "Name of Plan" | Get-SrmRecoveryPlanResult | + select -First 1 | Export-SrmRecoveryPlanResultAsXml + +### Protect a Replicated VM + +Goal: Take a VM replicated using vSphere Replication or Array Based Replication, add it to an appropriate protection group and configure it for protection + + $pg = Get-SrmProtectionGroup "Name of Protection Group" + Get-VM vm-01a | Protect-SrmVM -ProtectionGroup $pg diff --git a/Modules/Set-CBT.psm1 b/Modules/Set-CBT/Set-CBT.psm1 similarity index 100% rename from Modules/Set-CBT.psm1 rename to Modules/Set-CBT/Set-CBT.psm1 diff --git a/Modules/Start-UNMAP.psm1 b/Modules/Start-UNMAP/Start-UNMAP.psm1 similarity index 100% rename from Modules/Start-UNMAP.psm1 rename to Modules/Start-UNMAP/Start-UNMAP.psm1 diff --git a/Modules/VAMI/VAMI.psm1 b/Modules/VAMI/VAMI.psm1 new file mode 100755 index 0000000..92c5d5f --- /dev/null +++ b/Modules/VAMI/VAMI.psm1 @@ -0,0 +1,716 @@ +Function Get-VAMISummary { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves some basic information from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return basic VAMI summary info + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMISummary +#> + $systemVersionAPI = Get-CisService -Name 'com.vmware.appliance.system.version' + $results = $systemVersionAPI.get() | select product, type, version, build, install_time + + $systemUptimeAPI = Get-CisService -Name 'com.vmware.appliance.system.uptime' + $ts = [timespan]::fromseconds($systemUptimeAPI.get().toString()) + $uptime = $ts.ToString("hh\:mm\:ss\,fff") + + $summaryResult = [pscustomobject] @{ + Product = $results.product; + Type = $results.type; + Version = $results.version; + Build = $results.build; + InstallTime = $results.install_time; + Uptime = $uptime + } + $summaryResult +} + +Function Get-VAMIHealth { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves health information from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return VAMI health + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIHealth +#> + $healthOverall = (Get-CisService -Name 'com.vmware.appliance.health.system').get() + $healthLastCheck = (Get-CisService -Name 'com.vmware.appliance.health.system').lastcheck() + $healthCPU = (Get-CisService -Name 'com.vmware.appliance.health.load').get() + $healthMem = (Get-CisService -Name 'com.vmware.appliance.health.mem').get() + $healthSwap = (Get-CisService -Name 'com.vmware.appliance.health.swap').get() + $healthStorage = (Get-CisService -Name 'com.vmware.appliance.health.storage').get() + + # DB health only applicable for Embedded/External VCSA Node + $vami = (Get-CisService -Name 'com.vmware.appliance.system.version').get() + + if($vami.type -eq "vCenter Server with an embedded Platform Services Controller" -or $vami.type -eq "vCenter Server with an external Platform Services Controller") { + $healthVCDB = (Get-CisService -Name 'com.vmware.appliance.health.databasestorage').get() + } else { + $healthVCDB = "N/A" + } + $healthSoftwareUpdates = (Get-CisService -Name 'com.vmware.appliance.health.softwarepackages').get() + + $healthResult = [pscustomobject] @{ + HealthOverall = $healthOverall; + HealthLastCheck = $healthLastCheck; + HealthCPU = $healthCPU; + HealthMem = $healthMem; + HealthSwap = $healthSwap; + HealthStorage = $healthStorage; + HealthVCDB = $healthVCDB; + HealthSoftware = $healthSoftwareUpdates + } + $healthResult +} + +Function Get-VAMIAccess { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves access information from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return VAMI access interfaces (Console,DCUI,Bash Shell & SSH) + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIAccess +#> + $consoleAccess = (Get-CisService -Name 'com.vmware.appliance.access.consolecli').get() + $dcuiAccess = (Get-CisService -Name 'com.vmware.appliance.access.dcui').get() + $shellAccess = (Get-CisService -Name 'com.vmware.appliance.access.shell').get() + $sshAccess = (Get-CisService -Name 'com.vmware.appliance.access.ssh').get() + + $accessResult = New-Object PSObject -Property @{ + Console = $consoleAccess; + DCUI = $dcuiAccess; + BashShell = $shellAccess.enabled; + SSH = $sshAccess + } + $accessResult +} + +Function Get-VAMITime { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves the time and NTP info from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return current Time and NTP information + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMITime +#> + $systemTimeAPI = Get-CisService -Name 'com.vmware.appliance.system.time' + $timeResults = $systemTimeAPI.get() + + $timeSync = (Get-CisService -Name 'com.vmware.appliance.techpreview.timesync').get() + $timeSyncMode = $timeSync.mode + + $timeResult = [pscustomobject] @{ + Timezone = $timeResults.timezone; + Date = $timeResults.date; + CurrentTime = $timeResults.time; + Mode = $timeSyncMode; + NTPServers = "N/A"; + NTPStatus = "N/A"; + } + + if($timeSyncMode -eq "NTP") { + $ntpServers = (Get-CisService -Name 'com.vmware.appliance.techpreview.ntp').get() + $timeResult.NTPServers = $ntpServers.servers + $timeResult.NTPStatus = $ntpServers.status + } + $timeResult +} + +Function Get-VAMINetwork { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves network information from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return networking information including details for each interface + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMINetwork +#> + $netResults = @() + + $Hostname = (Get-CisService -Name 'com.vmware.appliance.networking.dns.hostname').get() + $dns = (Get-CisService -Name 'com.vmware.appliance.networking.dns.servers').get() + + Write-Host "Hostname: " $hostname + Write-Host "DNS Servers: " $dns.servers + + $interfaces = (Get-CisService -Name 'com.vmware.appliance.networking.interfaces').list() + foreach ($interface in $interfaces) { + $ipv4API = (Get-CisService -Name 'com.vmware.appliance.techpreview.networking.ipv4') + $spec = $ipv4API.Help.get.interfaces.CreateExample() + $spec+= $interface.name + $ipv4result = $ipv4API.get($spec) + + $interfaceResult = [pscustomobject] @{ + Inteface = $interface.name; + MAC = $interface.mac; + Status = $interface.status; + Mode = $ipv4result.mode; + IP = $ipv4result.address; + Prefix = $ipv4result.prefix; + Gateway = $ipv4result.default_gateway; + Updateable = $ipv4result.updateable + } + $netResults += $interfaceResult + } + $netResults +} + +Function Get-VAMIDisks { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves VMDK disk number to partition mapping VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return VMDK disk number to OS partition mapping + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIDisks +#> + $storageAPI = Get-CisService -Name 'com.vmware.appliance.system.storage' + $disks = $storageAPI.list() + + foreach ($disk in $disks | sort {[int]$_.disk.toString()}) { + $disk | Select Disk, Partition + } +} + +Function Start-VAMIDiskResize { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function triggers an OS partition resize after adding additional disk capacity + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function triggers OS partition resize operation + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Start-VAMIDiskResize +#> + $storageAPI = Get-CisService -Name 'com.vmware.appliance.system.storage' + Write-Host "Initiated OS partition resize operation ..." + $storageAPI.resize() +} + +Function Get-VAMIStatsList { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves list avialable monitoring metrics in VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return list of available monitoring metrics that can be queried + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIStatsList +#> + $monitoringAPI = Get-CisService -Name 'com.vmware.appliance.monitoring' + $ids = $monitoringAPI.list() | Select id | Sort-Object -Property id + + foreach ($id in $ids) { + $id + } +} + +Function Get-VAMIStorageUsed { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves the individaul OS partition storage utilization + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return individual OS partition storage utilization + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIStorageUsed +#> + $monitoringAPI = Get-CisService 'com.vmware.appliance.monitoring' + $querySpec = $monitoringAPI.help.query.item.CreateExample() + + # List of IDs from Get-VAMIStatsList to query + $querySpec.Names = @( + "storage.used.filesystem.autodeploy", + "storage.used.filesystem.boot", + "storage.used.filesystem.coredump", + "storage.used.filesystem.imagebuilder", + "storage.used.filesystem.invsvc", + "storage.used.filesystem.log", + "storage.used.filesystem.netdump", + "storage.used.filesystem.root", + "storage.used.filesystem.updatemgr", + "storage.used.filesystem.vcdb_core_inventory", + "storage.used.filesystem.vcdb_seat", + "storage.used.filesystem.vcdb_transaction_log", + "storage.totalsize.filesystem.autodeploy", + "storage.totalsize.filesystem.boot", + "storage.totalsize.filesystem.coredump", + "storage.totalsize.filesystem.imagebuilder", + "storage.totalsize.filesystem.invsvc", + "storage.totalsize.filesystem.log", + "storage.totalsize.filesystem.netdump", + "storage.totalsize.filesystem.root", + "storage.totalsize.filesystem.updatemgr", + "storage.totalsize.filesystem.vcdb_core_inventory", + "storage.totalsize.filesystem.vcdb_seat", + "storage.totalsize.filesystem.vcdb_transaction_log" + ) + + # Tuple (Filesystem Name, Used, Total) to store results + $storageStats = @{ + "autodeploy"=@{"name"="/storage/autodeploy";"used"=0;"total"=0}; + "boot"=@{"name"="/boot";"used"=0;"total"=0}; + "coredump"=@{"name"="/storage/core";"used"=0;"total"=0}; + "imagebuilder"=@{"name"="/storage/imagebuilder";"used"=0;"total"=0}; + "invsvc"=@{"name"="/storage/invsvc";"used"=0;"total"=0}; + "log"=@{"name"="/storage/log";"used"=0;"total"=0}; + "netdump"=@{"name"="/storage/netdump";"used"=0;"total"=0}; + "root"=@{"name"="/";"used"=0;"total"=0}; + "updatemgr"=@{"name"="/storage/updatemgr";"used"=0;"total"=0}; + "vcdb_core_inventory"=@{"name"="/storage/db";"used"=0;"total"=0}; + "vcdb_seat"=@{"name"="/storage/seat";"used"=0;"total"=0}; + "vcdb_transaction_log"=@{"name"="/storage/dblog";"used"=0;"total"=0} + } + + $querySpec.interval = "DAY1" + $querySpec.function = "MAX" + $querySpec.start_time = ((get-date).AddDays(-1)) + $querySpec.end_time = (Get-Date) + $queryResults = $monitoringAPI.query($querySpec) | Select * -ExcludeProperty Help + + foreach ($queryResult in $queryResults) { + # Update hash if its used storage results + if($queryResult.name -match "used") { + $key = (($queryResult.name).toString()).split(".")[-1] + $value = [Math]::Round([int]($queryResult.data[1]).toString()/1MB,2) + $storageStats[$key]["used"] = $value + # Update hash if its total storage results + } else { + $key = (($queryResult.name).toString()).split(".")[-1] + $value = [Math]::Round([int]($queryResult.data[1]).toString()/1MB,2) + $storageStats[$key]["total"] = $value + } + } + + $storageResults = @() + foreach ($key in $storageStats.keys | Sort-Object -Property name) { + $statResult = [pscustomobject] @{ + Filesystem = $storageStats[$key].name; + Used = $storageStats[$key].used; + Total = $storageStats[$key].total + } + $storageResults += $statResult + } + $storageResults +} + +Function Get-VAMIService { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves list of services in VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return list of services and their description + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIService + .EXAMPLE + Get-VAMIService -Name rbd +#> + param( + [Parameter( + Mandatory=$false, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$Name + ) + + if($Name -ne "") { + $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' + + try { + $serviceStatus = $vMonAPI.get($name,0) + $serviceString = [pscustomobject] @{ + Name = $name; + State = $serviceStatus.state; + Health = ""; + Startup = $serviceStatus.startup_type + } + if($serviceStatus.health -eq $null) { $serviceString.Health = "N/A"} else { $serviceString.Health = $serviceStatus.health } + $serviceString + } catch { + Write-Error $Error[0].exception.Message + } + } else { + $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' + $services = $vMonAPI.list_details() + + $serviceResult = @() + foreach ($key in $services.keys | Sort-Object -Property Value) { + $serviceString = [pscustomobject] @{ + Name = $key; + State = $services[$key].state; + Health = "N/A"; + Startup = $services[$key].Startup_type + } + if($services[$key].health -eq $null) { $serviceString.Health = "N/A"} else { $serviceString.Health = $services[$key].health } + + $serviceResult += $serviceString + } + $serviceResult + } +} + +Function Start-VAMIService { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves list of services in VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return list of services and their description + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Start-VAMIService -Name rbd +#> + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$Name + ) + + $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' + + try { + Write-Host "Starting $name service ..." + $vMonAPI.start($name) + } catch { + Write-Error $Error[0].exception.Message + } +} + +Function Stop-VAMIService { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves list of services in VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return list of services and their description + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Stop-VAMIService -Name rbd +#> + param( + [Parameter( + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$Name + ) + + $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' + + try { + Write-Host "Stopping $name service ..." + $vMonAPI.stop($name) + } catch { + Write-Error $Error[0].exception.Message + } +} + +Function Get-VAMIBackupSize { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves the backup size of the VCSA from VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to return the current backup size of the VCSA (common and core data) + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIBackupSize +#> + $recoveryAPI = Get-CisService 'com.vmware.appliance.recovery.backup.parts' + $backupParts = $recoveryAPI.list() | select id + + $estimateBackupSize = 0 + $backupPartSizes = "" + foreach ($backupPart in $backupParts) { + $partId = $backupPart.id.value + $partSize = $recoveryAPI.get($partId) + $estimateBackupSize += $partSize + $backupPartSizes += $partId + " data is " + $partSize + " MB`n" + } + + Write-Host "Estimated Backup Size: $estimateBackupSize MB" + Write-Host $backupPartSizes +} + +Function Get-VAMIUser { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function retrieves VAMI local users using VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to retrieve VAMI local users + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIUser +#> + param( + [Parameter( + Mandatory=$false, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [String]$Name + ) + + $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' + + $userResults = @() + + if($Name -ne "") { + try { + $user = $userAPI.get($name) + + $userString = [pscustomobject] @{ + User = $user.username + Name = $user.fullname + Email = $user.email + Status = $user.status + PasswordStatus = $user.passwordstatus + Role = $user.role + } + $userResults += $userString + } catch { + Write-Error $Error[0].exception.Message + } + } else { + $users = $userAPI.list() + + foreach ($user in $users) { + $userString = [pscustomobject] @{ + User = $user.username + Name = $user.fullname + Email = $user.email + Status = $user.status + PasswordStatus = $user.passwordstatus + Role = $user.role + } + $userResults += $userString + } + } + $userResults +} + +Function New-VAMIUser { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function to create new VAMI local user using VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to create a new VAMI local user + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + New-VAMIUser -name lamw -fullname "William Lam" -role "operator" -email "lamw@virtuallyghetto.com" -password "VMware1!" +#> + param( + [Parameter( + Mandatory=$true) + ] + [String]$name, + [Parameter( + Mandatory=$true) + ] + [String]$fullname, + [Parameter( + Mandatory=$true) + ] + [ValidateSet("admin","operator","superAdmin")][String]$role, + [Parameter( + Mandatory=$false) + ] + [String]$email="", + [Parameter( + Mandatory=$true) + ] + [String]$password + ) + + $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' + $createSpec = $userAPI.Help.add.config.CreateExample() + + $createSpec.username = $name + $createSpec.fullname = $fullname + $createSpec.role = $role + $createSpec.email = $email + $createSpec.password = [VMware.VimAutomation.Cis.Core.Types.V1.Secret]$password + + try { + Write-Host "Creating new user $name ..." + $userAPI.add($createSpec) + } catch { + Write-Error $Error[0].exception.Message + } +} + +Function Remove-VAMIUser { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .SYNOPSIS + This function to remove VAMI local user using VAMI interface (5480) + for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. + .DESCRIPTION + Function to remove VAMI local user + .EXAMPLE + Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! + Get-VAMIAccess +#> + param( + [Parameter( + Mandatory=$true) + ] + [String]$name, + [Parameter( + Mandatory=$false) + ] + [boolean]$confirm=$false + ) + + if(!$confirm) { + $answer = Read-Host -Prompt "Do you want to delete user $name (Y or N)" + if($answer -eq "Y" -or $answer -eq "y") { + $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' + + try { + Write-Host "Deleting user $name ..." + $userAPI.delete($name) + } catch { + Write-Error $Error[0].exception.Message + } + } + } +} \ No newline at end of file diff --git a/Modules/VCHA.psm1 b/Modules/VCHA/VCHA.psm1 similarity index 100% rename from Modules/VCHA.psm1 rename to Modules/VCHA/VCHA.psm1 diff --git a/Modules/VMCPFunctions.psm1 b/Modules/VMCPFunctions/VMCPFunctions.psm1 similarity index 100% rename from Modules/VMCPFunctions.psm1 rename to Modules/VMCPFunctions/VMCPFunctions.psm1 diff --git a/Modules/VMFSIncrease/VMFSIncrease.psm1 b/Modules/VMFSIncrease/VMFSIncrease.psm1 index ec0fa2b..90a8ccc 100644 --- a/Modules/VMFSIncrease/VMFSIncrease.psm1 +++ b/Modules/VMFSIncrease/VMFSIncrease.psm1 @@ -112,7 +112,7 @@ function Get-VmfsDatastoreIncrease Datastore = $Datastore.Name CanonicalName = $disk.CanonicalName Model = "$($disk.Vendor.TrimEnd(' ')).$($disk.Model.TrimEnd(' ')).$($disk.Revision.TrimEnd(' '))" - DiskSizeGB = $partInfo[0].Layout.Total.BlockSize * $hdPartInfo[0].Layout.Total.Block / 1GB + DiskSizeGB = $partInfo[0].Layout.Total.BlockSize * $partInfo[0].Layout.Total.Block / 1GB DiskBlocks = $partInfo[0].Layout.Total.Block DiskBlockMB = $partInfo[0].Layout.Total.BlockSize/1MB AvailableGB = [math]::Round($partMax - $partUsed, 2) @@ -181,7 +181,7 @@ function New-VmfsDatastoreIncrease { $lun = $hScsiDisk | where{ $_.CanonicalName -eq $dsOpt.Spec.Extent.DiskName } $partInfo = $hsSys.RetrieveDiskPartitionInfo($lun.DeviceName) - $partMax = ($vmfsExpOpt[0].Info.Layout.Partition | where{ $_.Type -eq 'VMFS' } | %{ ($_.End.Block - $_.Start.Block + 1) * $_.Start.BlockSize } | + $partMax = ($expOpt[0].Info.Layout.Partition | where{ $_.Type -eq 'VMFS' } | %{ ($_.End.Block - $_.Start.Block + 1) * $_.Start.BlockSize } | Measure-Object -Sum | select -ExpandProperty Sum)/1GB $partUsed = ($partInfo[0].Layout.Partition | where{ $_.Type -eq 'VMFS' } | %{ ($_.End.Block - $_.Start.Block + 1) * $_.Start.BlockSize } | Measure-Object -Sum | select -ExpandProperty Sum)/1GB diff --git a/Modules/VMToolsManagement/VMToolsManagement.psm1 b/Modules/VMToolsManagement/VMToolsManagement.psm1 new file mode 100644 index 0000000..df6e2e8 --- /dev/null +++ b/Modules/VMToolsManagement/VMToolsManagement.psm1 @@ -0,0 +1,1238 @@ +# Script Module : VMToolsManagement +# Version : 1.0 + +# Copyright © 2017 VMware, Inc. All Rights Reserved. + +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is furnished to do +# so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +New-VIProperty -Name ToolsBuildNumber -Object VirtualMachine -Value { + Param ($VM) + + foreach ($item in $VM.ExtensionData.Config.ExtraConfig.GetEnumerator()) { + if ($item.Key -eq "guestinfo.vmtools.buildNumber") { + $toolsBuildNumber = $item.value + break + } + } + + return $toolsBuildNumber +} -BasedOnExtensionProperty 'Config.ExtraConfig' -Force | Out-Null + +Function Get-VMToolsInfo { +<# +.SYNOPSIS + This advanced function retrieves the VMTools info of specified virtual machines. + +.DESCRIPTION + This advanced function retrieves the VMTools version and build number info of specified virtual machines. + +.PARAMETER VM + Specifies the virtual machines which you want to get the VMTools info of. + +.EXAMPLE + C:\PS> Import-Module .\VMToolsManagement.psm1 + C:\PS> $VCServer = Connect-VIServer -Server -User -Password + C:\PS> Get-VM -Server $VCServer | Get-VMToolsInfo + + Retrieves VMTools info of all virtual machines which run in the $VCServer vCenter Server. + +.EXAMPLE + C:\PS> Get-VM "*rhel*" | Get-VMToolsInfo + + Name ToolsVersion ToolsBuildNumber + ------ ------------ ---------------- + 111394-RHEL-6.8-0 10.2.0 6090153 + 111394-RHEL-6.8-1 9.0.15 + 111393-RHEL-Server-7.2 10.1.0 + + Retrieves VMTools info of virtual machines with name containing "rhel". + +.EXAMPLE + C:\PS> Get-VM -Location "MyClusterName" | Get-VMToolsInfo + + Retrieves VMTools info from virtual machines which run in the "MyClusterName" cluster. + +.EXAMPLE + C:\PS> Get-VMHost "MyESXiHostName" | Get-VM | Get-VMToolsInfo + + Retrieves VMTools info of virtual machines which run on the "MyESXiHostName" ESXi host. + +.NOTES + This advanced function assumes that you are connected to at least one vCenter Server system. + The tools build number is not supported in VMTools before 10.2.0 + +.NOTES + Author : Daoyuan Wang + Author email : daoyuanw@vmware.com + Version : 1.0 + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 (build 4564106) + VMware vCenter Server Version : 6.5 (build 4602587) + PowerCLI Version : PowerCLI 6.5 (build 4624819) + PowerShell Version : 5.1 +#> + + [CmdletBinding()] + + Param ( + [Parameter(Mandatory=$true, + ValueFromPipeLine = $true, + ValueFromPipelinebyPropertyName=$True, + Position = 0)] + [ValidateNotNullOrEmpty()] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]] $VM + ) + + Process { + Get-VM $VM | Select-Object Name, @{Name="ToolsVersion"; Expression={$_.Guest.ToolsVersion}}, ToolsBuildNumber + } +} + +Function Get-VMToolsInstallLastError { +<# +.SYNOPSIS + This advanced function retrieves the error code of last VMTools installation. + +.DESCRIPTION + This advanced function retrieves the error code of last VMTools installation on specified virtual machines. + +.PARAMETER VM + Specifies the virtual machines which you want to get the error code of. + +.EXAMPLE + C:\PS> Import-Module .\VMToolsManagement.psm1 + C:\PS> $VCServer = Connect-VIServer -Server -User -Password + C:\PS> Get-VM -Server $VCServer | Get-VMToolsInstallLastError + + Retrieves the last VMTools installation error code of all virtual machines which run in the $VCServer vCenter Server. + +.EXAMPLE + C:\PS> Get-VM "*win*" | Get-VMToolsInstallLastError + + Name LastToolsInstallErrCode + ------ ----------------------- + 111167-Win-7-Sp1-64-Enterprise-NoTools + 111323-Windows-8.1U3-32-Enterprise-Tools + 111305-Windows-Server2016 1641 + + Retrieves the last VMTools installation error code of virtual machines with name containing "win". + +.EXAMPLE + C:\PS> Get-VM -Location "MyClusterName" | Get-VMToolsInstallLastError + + Retrieves the last VMTools installation error code of virtual machines which run in the "MyClusterName" cluster. + +.EXAMPLE + C:\PS> Get-VMHost "MyESXiHostName" | Get-VM | Get-VMToolsInstallLastError + + Retrieves the last VMTools installation error code of virtual machines which run on the "MyESXiHostName" ESXi host. + +.NOTES + This advanced function assumes that you are connected to at least one vCenter Server system. + +.NOTES + Author : Daoyuan Wang + Author email : daoyuanw@vmware.com + Version : 1.0 + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 (build 4564106) + VMware vCenter Server Version : 6.5 (build 4602587) + PowerCLI Version : PowerCLI 6.5 (build 4624819)(build 4624819) + PowerShell Version : 5.1 +#> + + [CmdletBinding()] + + Param ( + [Parameter(Mandatory=$true, + ValueFromPipeLine = $true, + ValueFromPipelinebyPropertyName=$True, + Position = 0)] + [ValidateNotNullOrEmpty()] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]] $VM + ) + + Process { + $result = @() + foreach ($_ in $VM) { + $errorCodeInfo = $_.ExtensionData.Config.ExtraConfig.GetEnumerator() | Where-Object {$_.Key -eq "guestinfo.toolsInstallErrCode"} + + $info = New-Object PSObject + $info | Add-Member -type NoteProperty -name VmName -value $_.Name + $info | Add-Member -type NoteProperty -name LastToolsInstallErrCode -value $errorCodeInfo.Value + + $result += $info + } + $result + } +} + +Function Get-VMToolsGuestInfo { +<# +.SYNOPSIS + This advanced function retrieves the guest info of specified virtual machines. + +.DESCRIPTION + This advanced function retrieves the guest info such as HostName, IP, ToolsStatus, ToolsVersion, + ToolsInstallType and GuestFamily of specified virtual machines. + +.PARAMETER VM + Specifies the virtual machines which you want to get the guest info of. + +.EXAMPLE + C:\PS> Import-Module .\VMToolsManagement.psm1 + C:\PS> $VCServer = Connect-VIServer -Server -User -Password + C:\PS> Get-VM -Server $VCServer | Get-VMToolsGuestInfo + + Retrieves guest info of all virtual machines which run in the $VCServer vCenter Server. + +.EXAMPLE + C:\PS> Get-VM "*win*" | Get-VMToolsGuestInfo + + Name : 111323-Windows-8.1U3-32-Enterprise-Tools + HostName : win81u3 + IP : + ToolsStatus : toolsNotRunning + ToolsVersion : 10.2.0 + ToolsInstallType : guestToolsTypeMSI + GuestFamily : windowsGuest + VMPowerState : PoweredOff + + Name : 111305-Windows-Server2016 + HostName : WIN-ULETOOSSB7U + IP : 10.160.59.99 + ToolsStatus : toolsOk + ToolsVersion : 10.1.0 + ToolsInstallType : guestToolsTypeMSI + GuestFamily : windowsGuest + VMPowerState : PoweredOn + + Retrieves guest info of virtual machines with name containing "win". + +.EXAMPLE + C:\PS> Get-VM -Location "MyClusterName" | Get-VMToolsGuestInfo + + Retrieves guest info of virtual machines which run in the "MyClusterName" cluster. + +.EXAMPLE + C:\PS> Get-VMHost "MyESXiHostName" | Get-VM | Get-VMToolsGuestInfo + + Retrieves guest info of virtual machines which run on the "MyESXiHostName" ESXi host. + +.NOTES + This advanced function assumes that you are connected to at least one vCenter Server system. + +.NOTES + Author : Daoyuan Wang + Author email : daoyuanw@vmware.com + Version : 1.0 + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 (build 4564106) + VMware vCenter Server Version : 6.5 (build 4602587) + PowerCLI Version : PowerCLI 6.5 (build 4624819)(build 4624819) + PowerShell Version : 5.1 +#> + + [CmdletBinding()] + + Param ( + [Parameter(Mandatory=$true, + ValueFromPipeLine = $true, + ValueFromPipelinebyPropertyName=$True, + Position = 0)] + [ValidateNotNullOrEmpty()] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]] $VM + ) + + Process { + Get-VM $VM | Select-Object Name, @{Name="HostName"; Expression={$_.Guest.HostName}}, + @{Name="IP"; Expression={$_.Guest.ExtensionData.IpAddress}}, + @{Name="ToolsStatus"; Expression={$_.Guest.ExtensionData.ToolsStatus}}, + @{Name="ToolsVersion"; Expression={$_.Guest.ToolsVersion}}, + @{Name="ToolsInstallType"; Expression={$_.Guest.ExtensionData.ToolsInstallType}}, + @{Name="GuestFamily"; Expression={$_.Guest.GuestFamily}}, + PowerState + } +} + +Function Get-VMByToolsInfo { +<# +.SYNOPSIS + This advanced function retrieves the virtual machines with specified VMTools info. + +.DESCRIPTION + This advanced function retrieves the virtual machines with specified VMTools version, + running status or version status. + +.PARAMETER VM + Specifies the virtual machines which you want to query VMTools status of. + +.PARAMETER ToolsVersion + Specifies the VMTools version. + +.PARAMETER ToolsRunningStatus + Specifies the VMTools running status. + +.PARAMETER ToolsVersionStatus + Specifies the VMTools version status. + +.EXAMPLE + C:\PS> Import-Module .\VMToolsManagement.psm1 + C:\PS> $VCServer = Connect-VIServer -Server -User -Password + C:\PS> Get-VM -Server $VCServer | Get-VMByToolsInfo + + Retrieves the virtual machines with VMTools not running in vCenter Server $VCServer. + +.EXAMPLE + C:\PS> Get-VM | Get-VMByToolsInfo -ToolsRunningStatus guestToolsNotRunning + + Name PowerState Num CPUs MemoryGB + ---- ---------- -------- -------- + 111394-RHEL-6.8-1 PoweredOff 4 2.000 + + Retrieves all the virtual machines with VMTools not running. + +.EXAMPLE + C:\PS> Get-VM | Get-VMByToolsInfo -ToolsVersion '10.1.0' + + Name PowerState Num CPUs MemoryGB + ---- ---------- -------- -------- + 111394-RHEL-6.8-1 PoweredOff 4 2.000 + + Retrieves the virtual machines with VMTools version 10.1.0. + +.EXAMPLE + C:\PS> Get-VM -Location "MyClusterName" | Get-VMByToolsInfo -ToolsVersionStatus guestToolsNeedUpgrade + + Retrieves the virtual machines with VMTools that need to upgrade in the "MyClusterName" cluster. + +.EXAMPLE + C:\PS> Get-VMHost "MyESXiHostName" | Get-VM | Get-VMByToolsInfo -ToolsRunningStatus guestToolsRunning -ToolsVersionStatus guestToolsNeedUpgrade + + Retrieves the virtual machines with VMTools that need to upgrade on the "MyESXiHostName" ESXi host. + +.NOTES + This advanced function assumes that you are connected to at least one vCenter Server system. + +.NOTES + Author : Daoyuan Wang + Author email : daoyuanw@vmware.com + Version : 1.0 + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 (build 4564106) + VMware vCenter Server Version : 6.5 (build 4602587)(build 4602587) + PowerCLI Version : PowerCLI 6.5 (build 4624819) + PowerShell Version : 5.1 +#> + + [CmdletBinding()] + + Param ( + [Parameter(Mandatory=$true, + ValueFromPipeLine = $true, + ValueFromPipelinebyPropertyName=$True, + Position = 0)] + [ValidateNotNullOrEmpty()] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]] $VM, + + [Parameter(Mandatory=$false)] + [String] $ToolsVersion, + + [Parameter(Mandatory=$false)] + [ValidateSet("guestToolsRunning", + "guestToolsNotRunning", + "guestToolsExecutingScripts")] + [String] $ToolsRunningStatus, + + [Parameter(Mandatory=$false)] + [ValidateSet("guestToolsNotInstalled", + "guestToolsNeedUpgrade", + "guestToolsCurrent", + "guestToolsUnmanaged")] + [String] $ToolsVersionStatus + ) + + Process { + $vmList = Get-VM $VM + + if ((-not $ToolsVersion) -and (-not $ToolsRunningStatus) -and (-not $ToolsVersionStatus)) { + Throw "Please specify at lease one parameter: ToolsVersion, ToolsRunningStatus or ToolsVersionStatus" + } + + if ($ToolsVersion) { + $vmList = $vmList | Where-Object {$_.Guest.ToolsVersion -like $ToolsVersion} + } + + if ($ToolsRunningStatus) { + $vmList = $vmList | Where-Object {$_.Guest.ExtensionData.ToolsRunningStatus -eq $ToolsRunningStatus} + } + + if ($ToolsVersionStatus) { + $vmList = $vmList | Where-Object {$_.Guest.ExtensionData.ToolsVersionStatus -eq $ToolsVersionStatus} + } + + $vmList + } +} + +Function Get-VMToolsUpgradePolicy { +<# +.SYNOPSIS + This advanced function retrieves the VMTools upgrade policy info of specified virtual machines. + +.DESCRIPTION + This advanced function retrieves the VMTools upgrade policy info of specified virtual machines. + +.PARAMETER VM + Specifies the virtual machines which you want to query VMTools status of. + +.EXAMPLE + C:\PS> Get-VM "*rhel*" | Get-VMToolsUpgradePolicy + Name VMToolsUpgradePolicy + ------ ---------------------- + 111394-RHEL-6.8-0 manual + 111394-RHEL-6.8-1 manual + 111393-RHEL-Server-7.2 upgradeAtPowerCycle + Retrieves VMTools upgrade policy info of virtual machines with name containing "rhel". + +.EXAMPLE + C:\PS> Get-VM -Location "MyClusterName" | Get-VMToolsUpgradePolicy + Retrieves VMTools upgrade policy info from virtual machines which run in the "MyClusterName" cluster. + +.EXAMPLE + C:\PS> Get-VMHost "MyESXiHostName" | Get-VM | Get-VMToolsUpgradePolicy + Retrieves VMTools upgrade policyinfo of virtual machines which run on the "MyESXiHostName" ESXi host. + +.NOTES + This advanced function assumes that you are connected to at least one vCenter Server system. + +.NOTES + Author : Kyle Ruddy + Author email : kmruddy@gmail.com + Version : 1.0 + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 (build 7388607) + VMware vCenter Server Version : 6.5 (build 7312210) + PowerCLI Version : PowerCLI 6.5 (build 7155375) + PowerShell Version : 5.1 +#> + + [CmdletBinding()] + + Param ( + [Parameter(Mandatory=$true, + ValueFromPipeLine = $true, + ValueFromPipelinebyPropertyName=$True, + Position = 0)] + [ValidateNotNullOrEmpty()] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]] $VM + ) + + Process { + + Get-VM $VM | Select-Object Name, @{Name="VMToolsUpgradePolicy"; Expression={$_.ExtensionData.Config.Tools.ToolsUpgradePolicy}} + + } + +} + +Function Set-VMToolsUpgradePolicy { +<# +.SYNOPSIS + This advanced function sets the VMTool's upgrade policy to either "manual" or "upgradeAtPowerCycle". + +.DESCRIPTION + This advanced function sets the VMTool's upgrade policy to either "manual" or "upgradeAtPowerCycle" of specified virtual machines. + +.PARAMETER VM + Specifies the virtual machines which you want to set the VMTool's upgrade policy of. + +.EXAMPLE + C:\PS> Import-Module .\VMToolsManagement.psm1 + C:\PS> $VCServer = Connect-VIServer -Server -User -Password + C:\PS> Get-VM -Server $VCServer | Set-VMToolsUpgradePolicy -UpgradePolicy manual + + Sets VMTool's upgrade policy to "manual" of all virtual machines in the $VCServer vCenter Server. + +.EXAMPLE + C:\PS> Get-VM "*win*" | Set-VMToolsUpgradePolicy -UpgradePolicy upgradeAtPowerCycle + + Sets VMTool's upgrade policy to "upgradeAtPowerCycle" of virtual machines with name containing "win". + +.EXAMPLE + C:\PS> Get-VM -Location "MyClusterName" | Set-VMToolsUpgradePolicy -UpgradePolicy upgradeAtPowerCycle + + Sets VMTool's upgrade policy to "upgradeAtPowerCycle" of virtual machines in the "MyClusterName" cluster. + +.EXAMPLE + C:\PS> Get-VMHost "MyESXiHostName" | Get-VM | Set-VMToolsUpgradePolicy -UpgradePolicy manual + + Sets VMTool's upgrade policy to "manual" of virtual machines on the "MyESXiHostName" ESXi host. + +.NOTES + This advanced function assumes that you are connected to at least one vCenter Server system. + +.NOTES + Author : Daoyuan Wang + Author email : daoyuanw@vmware.com + Version : 1.1 + Update Author : Kyle Ruddy + Update email : kmruddy@gmail.com + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 (build 4564106)(build 7388607) + VMware vCenter Server Version : 6.5 (build 4602587)(build 7312210) + PowerCLI Version : PowerCLI 6.5 (build 4624819)(build 7155375) + PowerShell Version : 5.1 +#> + + [CmdletBinding(SupportsShouldProcess)] + + Param ( + [Parameter(Mandatory=$true, + ValueFromPipeLine = $true, + ValueFromPipelinebyPropertyName=$True, + Position = 0)] + [ValidateNotNullOrEmpty()] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]] $VM, + + [Parameter(Mandatory=$false, + Position = 1)] + [ValidateSet("upgradeAtPowerCycle", + "manual")] + [String] $UpgradePolicy + ) + Begin { + $vmConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec + $vmConfigSpec.Tools = New-Object VMware.Vim.ToolsConfigInfo + $vmConfigSpec.Tools.ToolsUpgradePolicy = $UpgradePolicy + } + + Process { + foreach ($_ in $VM) { + # Get current setting + $vmView = Get-View $_ -Property Config.Tools.ToolsUpgradePolicy + # Change if VMTools upgrade policy is not "upgradeAtPowerCycle" + if ($vmView.Config.Tools.ToolsUpgradePolicy -ne $UpgradePolicy) { + Write-Verbose "Applying 'upgradeAtPowerCycle' setting to $($_.Name)..." + $vmView.ReconfigVM($vmConfigSpec) + Get-VMToolsUpgradePolicy -VM $_ + } + } + } +} + +Function Invoke-VMToolsListProcessInVM { +<# +.Synopsis + This advanced function lists the processes in the virtual machine. + +.Description + This advanced function lists the running processes in the virtual machine. + +.PARAMETER VM + Specifies the virtual machine which you want to list the processes of. + +.Parameter GuestUser + Specifies the user name you want to use for authenticating with the guest OS. + +.Parameter GuestPassword + Specifies the password you want to use for authenticating with the guest OS. + +.Example + C:\PS> Import-Module .\VMToolsManagement.psm1 + C:\PS> $SampleVM = get-vm "MyVMName" + C:\PS> Invoke-VMToolsListProcessInVM -VM $SampleVM -GuestUser -GuestPassword + + ScriptOutput + ----------------------------------------------------------------------------------------------------------------------- + | USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND + | root 1 0.0 0.0 19360 1556 ? Ss Jul20 0:02 /sbin/init + | root 2 0.0 0.0 0 0 ? S Jul20 0:00 [kthreadd] + | root 3 0.0 0.0 0 0 ? S Jul20 0:06 [migration/0] + | root 4 0.0 0.0 0 0 ? S Jul20 0:00 [ksoftirqd/0] + | root 5 0.0 0.0 0 0 ? S Jul20 0:00 [stopper/0] + ...... + + List the processes in the "MyVMName" VM. + +.NOTES + This advanced function lists processes in the guest OS of virtual machine. + A VMTools should already be running in the guest OS. + +.NOTES + Author : Daoyuan Wang + Author email : daoyuanw@vmware.com + Version : 1.0 + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 (build 4564106) + VMware vCenter Server Version : 6.5 (build 4602587) + PowerCLI Version : PowerCLI 6.5 (build 4624819) + PowerShell Version : 5.1 + Guest OS : RHEL6.8, Windows7 +#> + + [CmdletBinding()] + + Param ( + [Parameter(Mandatory=$true, + ValueFromPipeLine = $true, + ValueFromPipelinebyPropertyName=$True, + Position = 0)] + [ValidateNotNullOrEmpty()] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM, + + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [String] $GuestUser, + + [Parameter(Mandatory=$true)] + [AllowEmptyString()] + [String] $GuestPassword + ) + + Process { + $vmView = Get-VM $VM | Get-View -Property Guest + if ($vmView.Guest.State -eq 'NotRunning') { + Write-Error "$VM is Not Running, unable to list the processes!" + return + } + + if ($vmView.Guest.GuestFamily -match 'windows') { + $command = 'Get-Process' + } elseif ($vmView.Guest.GuestFamily -match 'linux') { + $command = 'ps aux' + } else { + $command = 'ps' + } + + Invoke-VMScript -VM $VM -ScriptText $command -GuestUser $GuestUser -GuestPassword $GuestPassword + } +} + +Function Update-VMToolsImageLocation { +<# +.Synopsis + This advanced function updates the link /productLocker in ESXi host. + +.Description + This advanced function updates the link /productLocker in ESXi host directly to avoid host reboot. + +.Parameter VMHost + Specifies the ESXi host on which you want to update the /productLocker link. + +.Parameter HostUser + Specifies the user name you want to use for authenticating with the ESXi host. + +.Parameter HostPassword + Specifies the password you want to use for authenticating with the ESXi host. + +.Parameter ImageLocation + Specifies the new image location Where-Object you want /producterLocker to link. + +.Example + C:\PS> Import-Module .\VMToolsManagement.psm1 + C:\PS> $SampleHost = get-vmhost + C:\PS> Update-VMToolsImageLocation -VmHost $SampleHost -HostUser 'root' -HostPassword -ImageLocation '/locker/packages/6.5.0/' + + Update link /productLocker successfully. + + Update the link /producterLocker on $SampleHost to point to '/locker/packages/6.5.0/'. + +.NOTES + This advanced function connects to ESXi host to execute shell command directly. + Make sure the SSH service on ESXi host is enabled, and a SSH library(Posh-SSH or SSH-Sessions etc.) + for powershell is already installed on client Where-Object you call this advanced function. + You can instal Posh-SSH by executing: + iex (New-Object Net.WebClient).DownloadString("https://gist.github.com/darkoperator/6152630/raw/c67de4f7cd780ba367cccbc2593f38d18ce6df89/instposhsshdev") + For SSH-Sessions installation and usage, please refer to + http://www.powershelladmin.com/wiki/SSH_from_PowerShell_using_the_SSH.NET_library + + +.NOTES + Author : Daoyuan Wang + Author email : daoyuanw@vmware.com + Version : 1.0 + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 (build 4564106) + VMware vCenter Server Version : 6.5 (build 4602587) + PowerCLI Version : PowerCLI 6.5 (build 4624819) + PowerShell Version : 5.1 +#> + + [CmdletBinding()] + + Param ( + [Parameter(Mandatory=$true, + ValueFromPipeLine = $true, + ValueFromPipelinebyPropertyName=$True, + Position = 0)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost] $VMHost, + + [Parameter(Mandatory=$true)] + [String] $HostUser, + + [Parameter(Mandatory=$true)] + [AllowEmptyString()] + [String] $HostPassword, + + [Parameter(Mandatory=$true)] + [String] $ImageLocation + ) + + Process { + if (-not (Get-Command New-SSHSession)) { + Throw "This advanced function depends on SSH library. Please ensure a SSH library is already installed!" + } + + $password = new-object System.Security.SecureString + if ($HostPassword) { + $password = ConvertTo-SecureString -AsPlainText $HostPassword -Force + } + + $crendential = New-Object System.Management.Automation.PSCredential -ArgumentList $HostUser, $password + $sshSession = New-SSHSession -ComputerName $VMHost -Credential $crendential -Force + + $result = Invoke-SshCommand -SSHSession $sshSession -Command "readlink /productLocker" -EnsureConnection:$false + Write-Verbose "The link /productLocker before change: $($result.Output)" + + $command = "rm /productLocker && ln -s $ImageLocation /productLocker" + Write-Verbose "Updating /productLocker on $VMHost..." + $result = Invoke-SshCommand -SSHSession $sshSession -Command $command -EnsureConnection:$false + if ($result.ExitStatus -eq 0) { + Write-Host "Update link /productLocker successfully." -ForegroundColor Green + } else { + Write-Error "Failed to update link /productLocker: $($result.Error)" + } + + $result = Invoke-SshCommand -SSHSession $sshSession -Command "readlink /productLocker" -EnsureConnection:$false + Write-Verbose "The link /productLocker after change: $($result.Output)" + } +} + +Function Set-VMToolsConfInVM { +<# +.Synopsis + This advanced function sets the tools.conf content in guest OS. + +.Description + This advanced function copies the tools.conf in gueset OS of virtual machine to localhost, + then sets it locally by setting "vmtoolsd.level" to a valid level and copies it back to the guest OS. + +.PARAMETER VM + Specifies the virtual machine to update. + +.PARAMETER LogLevel + Specifies the desired log level to log. + +.Parameter GuestUser + Specifies the user name you want to use for authenticating with the guest OS. + +.Parameter GuestPassword + Specifies the password you want to use for authenticating with the guest OS. + +.Example + C:\PS> Import-Module .\VMToolsManagement.psm1 + C:\PS> $SampleVM = get-vm "MyVMName" + C:\PS> Update-VMToolsConfInVM -VM $SampleVM -GuestUser -GuestPassword + + Update tools.conf of 111394-RHEL-6.8-0 successfully. + + Updates the tools.conf in $SampleVM, changes the vmtoolsd log level to info ("vmtoolsd.level = info") for example. + +.NOTES + This advanced function updates the tools.conf in guest OS. A VMTools should already be running in the guest OS. + +.NOTES + Author : Daoyuan Wang + Author email : daoyuanw@vmware.com + Version : 1.1 + Update Author : Kyle Ruddy + Update email : kmruddy@gmail.com + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 (build 4564106)(build 7388607) + VMware vCenter Server Version : 6.5 (build 4602587)(build 7312210) + PowerCLI Version : PowerCLI 6.5 (build 4624819)(build 7155375) + PowerShell Version : 5.1 +#> + + [CmdletBinding()] + + Param ( + [Parameter(Mandatory=$true, + ValueFromPipeLine = $true, + ValueFromPipelinebyPropertyName=$True, + Position = 0)] + [ValidateNotNullOrEmpty()] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM, + + [Parameter(Mandatory=$true, + Position = 1)] + [ValidateSet("none", + "critical", + "error", + "warning", + "message", + "info", + "debug")] + [String] $LogLevel, + + [Parameter(Mandatory=$true)] + [String] $GuestUser, + + [Parameter(Mandatory=$true)] + [AllowEmptyString()] + [String] $GuestPassword + ) + + Process { + $vmGuest = Get-VMGuest $VM + $OsName = $vmGuest.OSFullName + $guestToolsConfFile = "" + $localToolsConfFile = ".\tools.conf" + + # Determine the tools.conf path in guest OS + if (($OsName -match "Linux") ` + -or ($OsName -match "FreeBSD") ` + -or ($OsName -match "Solaris")) { + $guestToolsConfFile = '/etc/vmware-tools/tools.conf' + } elseif (($OsName -match "Windows Server 2003") ` + -or ($OsName -match "Windows Server 2000") ` + -or ($OsName -match "Windows XP")) { + $guestToolsConfFile = 'C:\Documents and Settings\All Users\Application Data\VMware\VMware Tools\tools.conf' + } elseif ($OsName -match "Windows") { + $guestToolsConfFile = 'C:\ProgramData\VMware\VMware Tools\tools.conf' + } elseif ($OsName -match "Mac") { + $guestToolsConfFile = '/Library/Application Support/VMware Tools/tools.conf' + } else { + Throw "Unknown tools.conf path on OS: $OsName" + } + + # Get the tools.conf from guest OS to localhost, ignore the error if tools.conf was not found in guest OS + Write-Verbose "Copy tools.conf from $VM to localhost..." + $lastError = $Error[0] + Copy-VMGuestFile -Source $guestToolsConfFile -Destination $localToolsConfFile -VM $VM -GuestToLocal ` + -GuestUser $GuestUser -GuestPassword $GuestPassword -Force -ErrorAction:SilentlyContinue + + # The tools.conf doesn't exist in guest OS, create an empty one locally + if (($Error[0] -ne $lastError) -and ($Error[0] -notmatch 'tools.conf was not found')) { + Write-Error "Failed to copy tools.conf from $VM" + return + } elseif (-not (Test-Path $localToolsConfFile)) { + Set-Content $localToolsConfFile $null + } + + ############################################################################# + # Updates tools.conf by setting vmtoolsd.level = info, just for example. + ############################################################################# + $confContent = Get-Content $localToolsConfFile + $updatedContent = "vmtoolsd.level = $LogLevel" + + Write-Verbose "Editing tools.conf (set 'vmtoolsd.level = info' for example)..." + if ($confContent -match "vmtoolsd\.level") { + $confContent -replace "vmtoolsd\.level.*", $updatedContent | Set-Content $localToolsConfFile + } elseif ($confContent -match "logging") { + Add-Content $localToolsConfFile $updatedContent + } else { + Add-Content $localToolsConfFile "[logging]`nlog=true" + Add-Content $localToolsConfFile $updatedContent + } + + # Upload the changed tools.conf to guest OS + try { + Write-Verbose "Copy local tools.conf to $VM..." + Copy-VMGuestFile -Source $localToolsConfFile -Destination $guestToolsConfFile -VM $VM -LocalToGuest ` + -GuestUser $GuestUser -GuestPassword $GuestPassword -Force -ErrorAction:Stop + } catch { + Write-Error "Failed to update tools.conf of $VM" + Write-Verbose "Removing the local tools configuration file" + Remove-Item $localToolsConfFile + return + } + Write-Host "The tools.conf updated in $VM successfully." -ForegroundColor Green + Write-Verbose "Removing the local tools configuration file" + Remove-Item $localToolsConfFile + } +} + +Function Invoke-VMToolsVIBInstall { +<# +.SYNOPSIS + This advanced function installs VMTool VIB in ESXi hosts. + +.DESCRIPTION + This advanced function installs VMTool VIB in specified ESXi hosts. + +.PARAMETER VMHost + Specifies the ESXi hosts which you want to install VMTool VIB in. + +.PARAMETER ToolsVibUrl + Specifies the URL of VMTools VIB package which you want to install in ESXi hosts. + +.EXAMPLE + C:\PS> Import-Module .\VMToolsManagement.psm1 + C:\PS> $VCServer = Connect-VIServer -Server -User -Password . + C:\PS> $viBurl = "http:///VMware_locker_tools-light_6.5.0-10.2.0.6085460.vib" + C:\PS> Get-VMHost -Server $VCServer | Invoke-VMToolsVIBInstall -ToolsVibUrl $viBurl + + Install VMTool VIB in $VCServer. + +.EXAMPLE + C:\PS> Invoke-VMToolsVIBInstall -VMHost "MyESXiHostName" -ToolsVibUrl $viBurl + + Installs VMTools VIB package successfully. + + Installs VMTool VIB in the "MyESXiHostName" ESXi host. + +.EXAMPLE + C:\PS> Get-VMHost -Location "MyClusterName" | Invoke-VMToolsVIBInstall -ToolsVibUrl $vib + + Installs VMTool VIB in ESXi host of the "MyClusterName" cluster. + +.NOTES + This advanced function assumes that you are connected to at least one vCenter Server system. + +.NOTES + Author : Daoyuan Wang + Author email : daoyuanw@vmware.com + Version : 1.0 + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 (build 4564106) + VMware vCenter Server Version : 6.5 (build 4602587) + PowerCLI Version : PowerCLI 6.5 (build 4624819) + PowerShell Version : 5.1 +#> + + [CmdletBinding()] + + Param ( + [Parameter(Mandatory=$true, + ValueFromPipeLine = $true, + ValueFromPipelinebyPropertyName=$True, + Position = 0)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost[]] $VMHost, + + [Parameter(Mandatory=$true)] + [String] $ToolsVibUrl + ) + + Process { + foreach ($_ in $VMHost) { + $esxcli = Get-EsxCLI -VMHost $_ -V2 + + $result = $esxcli.software.vib.list.Invoke() | Where-Object {$_.name -match 'tools'} + Write-Verbose "Existing tools VIB on $_ before installing: $($result.Name)_$($result.Version)" + + # Install VIBs + Write-Verbose "Installing $ToolsVibUrl on $($_.Name)..." + $Error.Clear() + $cliArgs = $esxcli.software.vib.install.CreateArgs() + $cliArgs.viburl = $ToolsVibUrl + $cliArgs.nosigcheck = $true + $cliArgs.force = $true + $result = $esxcli.software.vib.install.Invoke($cliArgs) + if ($Error) { + Write-Error "Failed to install VMTools VIB package!" + } else { + Write-Verbose $result.Message + $result = $esxcli.software.vib.list.Invoke() | Where-Object {$_.name -match 'tools'} + Write-Verbose "Tools VIB on $_ after installing: $($result.Name)_$($result.Version)" + Write-Host "VMTools VIB package installed on $_ successfully." -ForegroundColor Green + } + } + } +} + +Function Invoke-VMToolsUpgradeInVMs { +<# +.SYNOPSIS + This advanced function upgrades VMTools to the version bundled by ESXi host. + +.DESCRIPTION + This advanced function upgrades VMTools of specified virtual machines to the version + bundled by ESXi host. You can also specify the number of virtual machines + to upgrade in parallel. + +.PARAMETER VM + Specifies the virtual machines you want to upgrade VMTools of. + +.PARAMETER GuestOSType + Specifies the guest OS type of the virtual machines. + +.PARAMETER VersionToUpgrade + Specifies the current running VMTools version of virtual machines. + +.PARAMETER MaxParallelUpgrades + Specifies the max virtual machine numbers to upgrade in parallel. + +.EXAMPLE + C:\PS> Import-Module .\VMToolsManagement.psm1 + C:\PS> $VCServer = Connect-VIServer -Server -User -Password + C:\PS> Get-VM -Server $VCServer | Invoke-VMToolsUpgradeInVMs -MaxParallelUpgrades 5 + + Upgrades VMTools of all virtual machines in the $VCServer vCenter Server, 5 at a time in parallel. + +.EXAMPLE + C:\PS> Get-VM | Invoke-VMToolsUpgradeInVMs -GuestOSType windows -MaxParallelUpgrades 1 | ft -Autosize + + Upgrade result: + + VmName UpgradeResult ToolsVersion ToolsVersionStatus TotalSeconds Message + ------ ------------- ------------ ------------------ ------------ ------- + 111167-Win-7-Sp1-64-Enterprise-NoTools-2 Completed 10.1.0 guestToolsCurrent 102 Upgrade VMTools successfully + 111393-RHEL-Server-7.2 Skipped 10.0.0 guestToolsNeedUpgrade 0 Guest OS type does not meet condtion 'windows' + 111305-Windows-Server2016 Completed 10.1.0 guestToolsCurrent 144 Upgrade VMTools successfully + + Upgrades VMTools of windows virtual machines one by one. + +.EXAMPLE + C:\PS> Get-VM -Location "MyClusterName" | Invoke-VMToolsUpgradeInVMs -MaxParallelUpgrades 2 | ft -Autosize + + Upgrade result: + + VmName UpgradeResult ToolsVersion ToolsVersionStatus TotalSeconds Message + ------ ------------- ------------ ------------------ ------------ ------- + 111167-Win-7-Sp1-64-Enterprise-NoTools-2 Failed 10.0.0 guestToolsNeedUpgrade 0 The required VMware Tools ISO image does not exist or is inaccessible. + 111393-RHEL-Server-7.2 Completed 10.1.0 guestToolsCurrent 100 Upgrade VMTools successfully + + Upgrades VMTools of virtual machines in the "MyClusterName" cluster, 2 at a time. + +.EXAMPLE + C:\PS> Get-VMHost "MyESXiHostName" | Get-VM | Invoke-VMToolsUpgradeInVMs -MaxParallelUpgrades 5 + + Upgrades VMTools of virtual machines on the "MyESXiHostName" ESXi host, 5 at a time. + +.NOTES + This advanced function assumes an old VMTools is already running in the virtual machine. + +.NOTES + Author : Daoyuan Wang + Author email : daoyuanw@vmware.com + Version : 1.0 + ==========Tested Against Environment========== + VMware vSphere Hypervisor(ESXi) Version : 6.5 (build 4564106) + VMware vCenter Server Version : 6.5 (build 4602587) + PowerCLI Version : PowerCLI 6.5 (build 4624819) + PowerShell Version : 5.1 +#> + + [CmdletBinding()] + + Param ( + [Parameter(Mandatory=$true, + ValueFromPipeLine = $true, + ValueFromPipelinebyPropertyName=$True, + Position = 0)] + [ValidateNotNullOrEmpty()] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]] $VM, + + [Parameter(Mandatory=$false)] + [ValidateSet("linux", "windows")] + [String] $GuestOSType, + + [Parameter(Mandatory=$false)] + [String] $VersionToUpgrade, + + [Parameter(Mandatory=$false)] + [ValidateRange(1, 5)] + [Int] $MaxParallelUpgrades = 1 + ) + + Begin { + $RunspacePool = [runspacefactory]::CreateRunspacePool( + 1, #Min Runspaces + $MaxParallelUpgrades #Max Runspaces + ) + + $RunspacePool.Open() + + $jobs = New-Object System.Collections.ArrayList + $result = @() + } + + Process { + foreach ($_ in $VM) { + $vmView = Get-View $_ -Property Guest + $toolsVersion = $_.Guest.ToolsVersion + $toolsVersionStatus = $vmView.Guest.ToolsVersionStatus + + # Skip if VMTools doesn't need to upgrade + if ($toolsVersionStatus -ne "guestToolsNeedUpgrade") { + Write-Host "No VMTools need to upgrade!`nVM: '$_', ToolsVersionStatus: '$toolsVersionStatus'" + $result += [pscustomobject]@{ + VmName = $_.Name + UpgradeResult = "Skipped" + ToolsVersion = $toolsVersion + ToolsVersionStatus = $toolsVersionStatus + TotalSeconds = 0 + Message = "No VMTools need to upgrade!" + } + continue + } + + # Skip if current VMTools doesn't meet to specified version + if ($VersionToUpgrade -and ($toolsVersion -notmatch $VersionToUpgrade)) { + Write-Host "Current ToolsVersion in $_ is: $toolsVersion,"` + "does not meet condtion `'$VersionToUpgrade`', skipping it..." -ForegroundColor Yellow + $result += [pscustomobject]@{ + VmName = $_.Name + UpgradeResult = "Skipped" + ToolsVersion = $toolsVersion + ToolsVersionStatus = $toolsVersionStatus + TotalSeconds = 0 + Message = "Current VMTools version does not meet condtion `'$VersionToUpgrade`'" + } + continue + } + + # Create a thread to upgrade VMTools for each virtual machine + $PSThread = [powershell]::Create() + $PSThread.RunspacePool = $RunspacePool + + # Script content to upgrade VMTools + $PSThread.AddScript({ + Param ( + $vcServer, + $session, + $vmId, + $GuestOSType + ) + # Load PowerCLI module and connect to VCServer, as child thread environment is independent with parent + if(-not $global:DefaultVIServer) { + $moduleName = "vmware.vimautomation.core" + if(-not (Get-Module | Where-Object {$_.name -eq $moduleName})) { + try { + Import-Module $moduleName -ErrorAction SilentlyContinue | Out-Null + } + catch { + Throw "Failed to load PowerCLI module('$moduleName')" + } + } + try { + $server = Connect-VIServer -Server $vcserver -session $session -Force + } + catch { + Throw "Failed to connect to VI server: $vcserver" + } + } + + # Retrieves VM + $vm = Get-VM -Id $vmId + + $ThreadID = [appdomain]::GetCurrentThreadId() + Write-Verbose “Thread[$ThreadID]: Beginning Update-Tools for $vm” + + if ($vm.PowerState -ne 'PoweredOn') { + Write-Host "Powering on VM: $vm..." + Start-VM $vm | Out-Null + $vm = Get-VM $vm + } + + # Wait for OS and VMTools starting up + $timeOut = 60*10 #seconds + $refreshInterval = 5 #seconds + $count = $timeOut/$refreshInterval + while (($vm.Guest.ExtensionData.ToolsRunningStatus -ne "guestToolsRunning") ` + -or (-not $vm.Guest.GuestFamily)) { + $count -= 1 + if ($count -lt 0) { + Write-Error "VMTools doesn't start up in $timeOut seconds, please check if $vm is hung!" + break + } + Write-Verbose "Waiting for VMTools running in $vm before upgrading..." + Start-Sleep -Seconds $refreshInterval + } + + # Skip if virtual machine doesn't meet specified guest OS type + if ($GuestOSType -and ($vm.Guest.GuestFamily -notmatch $GuestOSType)) { + Write-Host "GuestFamily of $vm is: $($vm.Guest.GuestFamily),"` + "does not meet condition `'$GuestOSType`', skipping it..." -ForegroundColor Yellow + # upgrade result + [pscustomobject]@{ + VmName = $vm.Name + UpgradeResult = "Skipped" + ToolsVersion = $vm.Guest.ToolsVersion + ToolsVersionStatus = $vm.Guest.ExtensionData.ToolsVersionStatus + TotalSeconds = 0 + Message = "Guest OS type does not meet condtion `'$GuestOSType`'" + } + Disconnect-VIServer $server -Confirm:$false + return + } + + # Upgrade VMTools and check the tools version status + Write-Host "Upgrading VMTools for VM: $vm..." + $task = Update-Tools -VM $vm -RunAsync + $task | Wait-Task + $task = Get-Task -Id $task.Id + + if ($task.State -eq "Success") { + $upgradeResult = "Completed" + $message = "Upgrade VMTools successfully" + Write-Host "Upgrade VMTools successfully for VM: $vm" -ForegroundColor Green + } else { + $upgradeResult = "Failed" + $message = $task.ExtensionData.Info.Error.LocalizedMessage + Write-Error "Failed to upgrade VMTools for VM: $vm" + } + $vm = Get-VM $vm + # Upgrade result to return + [pscustomobject]@{ + VmName = $vm.Name + UpgradeResult = $upgradeResult + ToolsVersion = $vm.Guest.ToolsVersion + ToolsVersionStatus = $vm.Guest.ExtensionData.ToolsVersionStatus + TotalSeconds = [math]::Floor(($task.FinishTime).Subtract($task.StartTime).TotalSeconds) + Message = $message + } + Write-Verbose “Thread[$ThreadID]: Ending Update-Tools for $vm” + }) | Out-Null + $vc = $Global:DefaultVIServer.ServiceUri.Host + $vcSession = $Global:DefaultVIServer.SessionSecret + $PSThread.AddArgument($vc).AddArgument($vcSession).AddArgument($_.Id).AddArgument($GuestOSType) | Out-Null + + # Start thread + $Handle = $PSThread.BeginInvoke() + $job = New-Object System.Object + $job | Add-Member -type NoteProperty -name Thread -value $PSThread + $job | Add-Member -type NoteProperty -name Handle -value $Handle + $jobs.Add($job) | Out-Null + + Write-Verbose (“Available Runspaces in RunspacePool: {0}” -f $RunspacePool.GetAvailableRunspaces()) + } + } + + End { + #Verify all threads completed + while (($jobs | Where-Object {$_.Handle.iscompleted -ne "Completed"}).Count -gt 0) { + Start-Sleep -Seconds 5 + } + + $upgradeResult = $jobs | foreach { + $_.Thread.EndInvoke($_.Handle) + $_.Thread.Dispose() + } + $result += $upgradeResult + $result + + $RunspacePool.Close() + $RunspacePool.Dispose() + } +} + +Export-ModuleMember *-* diff --git a/Modules/VMware-vCD-Module/LICENSE b/Modules/VMware-vCD-Module/LICENSE new file mode 100644 index 0000000..0fa220a --- /dev/null +++ b/Modules/VMware-vCD-Module/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Markus Kraus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Modules/VMware-vCD-Module/README.md b/Modules/VMware-vCD-Module/README.md new file mode 100644 index 0000000..3d7bc48 --- /dev/null +++ b/Modules/VMware-vCD-Module/README.md @@ -0,0 +1,40 @@ +VMware-vCD-Module PowerShell Module +=================================== + +![Invoke-MyOnBoarding](/media/Invoke-MyOnBoarding.png) + +# About + +## Project Owner: + +Markus Kraus [@vMarkus_K](https://twitter.com/vMarkus_K) + +MY CLOUD-(R)EVOLUTION [mycloudrevolution.com](http://mycloudrevolution.com/) + + +## Project WebSite: +[PowerCLI vCloud Director Customer Provisioning](https://mycloudrevolution.com/2017/06/13/powercli-vcloud-director-customer-provisioning/) + +[PowerCLI – Create vCloud Director Edge Gateway](https://mycloudrevolution.com/2017/06/27/powercli-create-vcloud-director-edge-gateway/) + + +## Project Documentation: + +[Read the Docs - VMware-vCD-Module](http://vmware-vcd-module.readthedocs.io/) + +## Project Description: + +The 'VMware-vCD-Module' PowerShell Module is focused on the initial creation of VMware vCloud Director Objects like Org, Org User, Org VDC with External Networks or Edge Gateway. + +All Functions in this Module can be used as standalone Cmdlet but also the ``Invoke-My OnBoarding`` Functions to process a JSON File and create all Objects at once. + +### Fully tested Versions: + +Powershell: v4, v5 + +PowerCLI: 6.5.1 + +VMware vCloud Director: 8.10.1 + + + diff --git a/Modules/VMware-vCD-Module/VMware-vCD-Module.psd1 b/Modules/VMware-vCD-Module/VMware-vCD-Module.psd1 new file mode 100644 index 0000000..bf67094 --- /dev/null +++ b/Modules/VMware-vCD-Module/VMware-vCD-Module.psd1 @@ -0,0 +1,129 @@ +# +# Modulmanifest f�r das Modul "PSGet_VMware-vCD-Module" +# +# Generiert von: Markus +# +# Generiert am: 6/11/2017 +# + +@{ + +# Die diesem Manifest zugeordnete Skript- oder Bin�rmoduldatei. +# RootModule = '' + +# Die Versionsnummer dieses Moduls +ModuleVersion = '1.3.0' + +# ID zur eindeutigen Kennzeichnung dieses Moduls +GUID = '1ef8a2de-ca22-4c88-8cdb-e00f35007d2a' + +# Autor dieses Moduls +Author = 'Markus Kraus' + +# Unternehmen oder Hersteller dieses Moduls +CompanyName = 'mycloudrevolution.com' + +# Urheberrechtserkl�rung f�r dieses Modul +Copyright = '(c) 2017 Markus. Alle Rechte vorbehalten.' + +# Beschreibung der von diesem Modul bereitgestellten Funktionen +Description = 'This a POwerShell Module based on VMware PowerCLI vCloud Director Module to extend its function' + +# Die f�r dieses Modul mindestens erforderliche Version des Windows PowerShell-Moduls +# PowerShellVersion = '' + +# Der Name des f�r dieses Modul erforderlichen Windows PowerShell-Hosts +# PowerShellHostName = '' + +# Die f�r dieses Modul mindestens erforderliche Version des Windows PowerShell-Hosts +# PowerShellHostVersion = '' + +# Die f�r dieses Modul mindestens erforderliche Microsoft .NET Framework-Version +# DotNetFrameworkVersion = '' + +# Die f�r dieses Modul mindestens erforderliche Version der CLR (Common Language Runtime) +# CLRVersion = '' + +# Die f�r dieses Modul erforderliche Prozessorarchitektur ("Keine", "X86", "Amd64"). +# ProcessorArchitecture = '' + +# Die Module, die vor dem Importieren dieses Moduls in die globale Umgebung geladen werden m�ssen +RequiredModules = @('VMware.VimAutomation.Cloud') + +# Die Assemblys, die vor dem Importieren dieses Moduls geladen werden m�ssen +# RequiredAssemblies = @() + +# Die Skriptdateien (PS1-Dateien), die vor dem Importieren dieses Moduls in der Umgebung des Aufrufers ausgef�hrt werden. +# ScriptsToProcess = @() + +# Die Typdateien (.ps1xml), die beim Importieren dieses Moduls geladen werden sollen +# TypesToProcess = @() + +# Die Formatdateien (.ps1xml), die beim Importieren dieses Moduls geladen werden sollen +# FormatsToProcess = @() + +# Die Module, die als geschachtelte Module des in "RootModule/ModuleToProcess" angegebenen Moduls importiert werden sollen. +NestedModules = @('functions\Invoke-MyOnBoarding.psm1', + 'functions\New-MyEdgeGateway.psm1', + 'functions\New-MyOrg.psm1', + 'functions\New-MyOrgAdmin.psm1', + 'functions\New-MyOrgVdc.psm1', + 'functions\New-MyOrgNetwork.psm1' + ) + +# Aus diesem Modul zu exportierende Funktionen +FunctionsToExport = 'Invoke-MyOnBoarding', 'New-MyEdgeGateway', 'New-MyOrg', 'New-MyOrgAdmin', 'New-MyOrgVdc', 'New-MyOrgNetwork' + +# Aus diesem Modul zu exportierende Cmdlets +CmdletsToExport = '*' + +# Die aus diesem Modul zu exportierenden Variablen +VariablesToExport = '*' + +# Aus diesem Modul zu exportierende Aliase +AliasesToExport = '*' + +# Aus diesem Modul zu exportierende DSC-Ressourcen +# DscResourcesToExport = @() + +# Liste aller Module in diesem Modulpaket +# ModuleList = @() + +# Liste aller Dateien in diesem Modulpaket +# FileList = @() + +# Die privaten Daten, die an das in "RootModule/ModuleToProcess" angegebene Modul �bergeben werden sollen. Diese k�nnen auch eine PSData-Hashtabelle mit zus�tzlichen von PowerShell verwendeten Modulmetadaten enthalten. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('VMware', 'vCloud', 'PowerCLI', 'vCloudDirector', 'Automation', 'EdgeGateway', 'OrgNetwork') + + # A URL to the license for this module. + LicenseUri = 'https://github.com/mycloudrevolution/VMware-vCD-Module/blob/master/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/mycloudrevolution/VMware-vCD-Module' + + # A URL to an icon representing this module. + IconUri = 'https://github.com/mycloudrevolution/VMware-vCD-Module/blob/master/media/vCD_Small.png' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # External dependent modules of this module + ExternalModuleDependencies = 'VMware.VimAutomation.Cloud' + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo-URI dieses Moduls +# HelpInfoURI = '' + +# Standardpr�fix f�r Befehle, die aus diesem Modul exportiert werden. Das Standardpr�fix kann mit "Import-Module -Prefix" �berschrieben werden. +# DefaultCommandPrefix = '' + +} + diff --git a/Modules/VMware-vCD-Module/examples/OnBoarding.json b/Modules/VMware-vCD-Module/examples/OnBoarding.json new file mode 100644 index 0000000..9fe4217 --- /dev/null +++ b/Modules/VMware-vCD-Module/examples/OnBoarding.json @@ -0,0 +1,30 @@ +{ +"Org": { + "Name":"TestOrg", + "FullName": "Test Org", + "Description":"Automation Test Org" + }, +"OrgAdmin": { + "Name":"TestOrgAdmin", + "Pasword": "myPassword1!", + "FullName":"Test OrgAdmin", + "EmailAddress":"test@admin.org" + }, +"OrgVdc": { + "Name":"TestOrgVdc", + "FixedSize": "M", + "CPULimit": "1000", + "MEMLimit":"1024", + "StorageLimit":"1024", + "StorageProfile":"Standard-DC01", + "ProviderVDC":"Provider-VDC-DC01", + "NetworkPool":"Provider-VDC-DC01-NetPool", + "ExternalNetwork": "External-OrgVdcNet", + "EdgeGateway": "Yes", + "IPAddress":"192.168.100.1", + "SubnetMask":"255.255.255.0", + "Gateway":"192.168.100.254", + "IPRangeStart":"192.168.100.2", + "IPRangeEnd":"192.168.100.3" + } +} \ No newline at end of file diff --git a/Modules/VMware-vCD-Module/functions/Invoke-MyOnBoarding.psm1 b/Modules/VMware-vCD-Module/functions/Invoke-MyOnBoarding.psm1 new file mode 100644 index 0000000..4d10fba --- /dev/null +++ b/Modules/VMware-vCD-Module/functions/Invoke-MyOnBoarding.psm1 @@ -0,0 +1,193 @@ +Function Invoke-MyOnBoarding { +<# +.SYNOPSIS + Creates all vCD Objecst for a new IAAS Customer + +.DESCRIPTION + Creates all vCD Objects for a new IAAS Customer + + All Objects are: + * Org + * Default Org Admin + * Org VDC + ** Private Catalog + ** Optional Bridged Network + + JSON Config Example: + + { + "Org": { + "Name":"TestOrg", + "FullName": "Test Org", + "Description":"Automation Test Org" + }, + "OrgAdmin": { + "Name":"TestOrgAdmin", + "Pasword": "myPassword1!", + "FullName":"Test OrgAdmin", + "EmailAddress":"test@admin.org" + }, + "OrgVdc": { + "Name":"TestOrgVdc", + "FixedSize": "M", + "CPULimit": "1000", + "MEMLimit":"1000", + "StorageLimit":"1000", + "StorageProfile":"Standard-DC01", + "ProviderVDC":"Provider-VDC-DC01", + "NetworkPool":"Provider-VDC-DC01-NetPool", + "ExternalNetwork": "External_OrgVdcNet", + "EdgeGateway": "Yes", + "IPAddress":"192.168.100.1", + "SubnetMask":"255.255.255.0", + "Gateway":"192.168.100.254", + "IPRangeStart":"192.168.100.2", + "IPRangeEnd":"192.168.100.3" + } + } + +.NOTES + File Name : Invoke-MyOnBoarding.ps1 + Author : Markus Kraus + Version : 1.3 + State : Ready + +.LINK + https://mycloudrevolution.com/ + +.EXAMPLE + Invoke-MyOnBoarding -ConfigFile ".\OnBoarding.json" -Enabled:$true + +.EXAMPLE + Invoke-MyOnBoarding -ConfigFile ".\OnBoarding.json" -Enabled:$false + +.PARAMETER ConfigFile + Full Path to the JSON Config File + +.PARAMETER Enabled + Should the Customer be enabled after creation + + Default: $False + +#> + Param ( + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Full Path to the JSON Config File")] + [ValidateNotNullorEmpty()] + [String] $ConfigFile, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="Should the Customer be enabled after creation")] + [ValidateNotNullorEmpty()] + [Switch]$Enabled + ) + Process { + + $Valid = $true + + Write-Verbose "## Import JSON Config" + Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Importing JSON Config...`n" + $Configs = Get-Content -Raw -Path $ConfigFile -ErrorAction Continue | ConvertFrom-Json -ErrorAction Continue + + if (!($Configs)) { + $Valid = $false + Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Importing JSON Config Failed" -ForegroundColor Red + } + else { + Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Importing JSON Config OK" -ForegroundColor Green + } + + if ($Valid) { + try{ + Write-Verbose "## Create Org" + Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new Org...`n" -ForegroundColor Yellow + $Trash = New-MyOrg -Name $Configs.Org.Name -FullName $Configs.Org.Fullname -Description $Configs.Org.Description -Enabled:$Enabled + Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new Org OK" -ForegroundColor Green + Get-Org -Name $Configs.Org.Name | Select-Object Name, FullName, Enabled | Format-Table -AutoSize + } + catch { + $Valid = $false + Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new Org Failed" -ForegroundColor Red + } + } + + if ($Valid) { + try{ + Write-Verbose "## Create OrgAdmin" + Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgAdmin...`n" -ForegroundColor Yellow + $Trash = New-MyOrgAdmin -Name $Configs.OrgAdmin.Name -Pasword $Configs.OrgAdmin.Pasword -FullName $Configs.OrgAdmin.FullName -EmailAddress $Configs.OrgAdmin.EmailAddress -Org $Configs.Org.Name -Enabled:$Enabled + Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgAdmin OK" -ForegroundColor Green + Get-CIUser -Org $Configs.Org.Name -Name $Configs.OrgAdmin.Name | Select-Object Name, FullName, Email | Format-Table -AutoSize + } + catch { + $Valid = $false + Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgAdmin Failed" -ForegroundColor Red + } + } + if ($Valid) { + try{ + Write-Verbose "## Create OrgVdc" + Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgVdc...`n" -ForegroundColor Yellow + + if ($Configs.OrgVdc.FixedSize){ + + Write-Host "Fixed Size (T-Shirt Size) '$($Configs.OrgVdc.FixedSize)' Org VDC Requested!" + + switch ($Configs.OrgVdc.FixedSize) { + M { + [String]$CPULimit = 36000 + [String]$MEMLimit = 122880 + [String]$StorageLimit = 1048576 + } + L { + [String]$CPULimit = 36000 + [String]$MEMLimit = 245760 + [String]$StorageLimit = 1048576 + } + default {throw "Invalid T-Shirt Size!"} + } + + } + else{ + Write-Host "Custom Org VDC Size Requested!" + + $CPULimit = $Configs.OrgVdc.CPULimit + $MEMLimit = $Configs.OrgVdc.MEMLimit + $StorageLimit = $Configs.OrgVdc.StorageLimit + + } + + if ($Configs.OrgVdc.ExternalNetwork -and $Configs.OrgVdc.EdgeGateway -like "Yes"){ + Write-Host "Edge Gateway for Org VDC '$($Configs.OrgVdc.Name)' Requested!" + $Trash = New-MyOrgVdc -Name $Configs.OrgVdc.Name -CPULimit $CPULimit -MEMLimit $MEMLimit -StorageLimit $StorageLimit -Networkpool $Configs.OrgVdc.NetworkPool ` + -StorageProfile $Configs.OrgVdc.StorageProfile -ProviderVDC $Configs.OrgVdc.ProviderVDC -Org $Configs.Org.Name -Enabled:$Enabled + + $EdgeName = $Configs.Org.Name + "-ESG01" + $Trash = New-MyEdgeGateway -Name $EdgeName -OrgVDCName $Configs.OrgVdc.Name -Orgname $Configs.Org.Name -ExternalNetwork $Configs.OrgVdc.ExternalNetwork ` + -IPAddress $Configs.OrgVdc.IPAddress -SubnetMask $Configs.OrgVdc.SubnetMask -Gateway $Configs.OrgVdc.Gateway -IPRangeStart $Configs.OrgVdc.IPRangeStart -IPRangeEnd $Configs.OrgVdc.IPRangeEnd + } + elseif ($Configs.OrgVdc.ExternalNetwork -and $Configs.OrgVdc.EdgeGateway -like "No"){ + Write-Host "External Network for Org VDC '$($Configs.OrgVdc.Name)' Requested!" + $Trash = New-MyOrgVdc -Name $Configs.OrgVdc.Name -CPULimit $CPULimit -MEMLimit $MEMLimit -StorageLimit $StorageLimit -Networkpool $Configs.OrgVdc.NetworkPool ` + -StorageProfile $Configs.OrgVdc.StorageProfile -ProviderVDC $Configs.OrgVdc.ProviderVDC -ExternalNetwork $Configs.OrgVdc.ExternalNetwork -Org $Configs.Org.Name -Enabled:$Enabled + } + else { + Write-Host "No external Connection for Org VDC '$($Configs.OrgVdc.Name)' Requested!" + $Trash = New-PecOrgVdc -Name $Configs.OrgVdc.Name -CPULimit $CPULimit -MEMLimit $MEMLimit -StorageLimit $StorageLimit -Networkpool $ProVdcNetworkPool.Name ` + -StorageProfile $Configs.OrgVdc.StorageProfile -ProviderVDC $Configs.OrgVdc.ProviderVDC -Org $Configs.Org.Name -Enabled:$Enabled + } + Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgVdc OK" -ForegroundColor Green + Get-OrgVdc -Org $Configs.Org.Name -Name $Configs.OrgVdc.Name | Select-Object Name, Enabled, CpuAllocationGhz, MemoryLimitGB, StorageLimitGB, AllocationModel, ThinProvisioned, UseFastProvisioning, ` + @{N="StorageProfile";E={$_.ExtensionData.VdcStorageProfiles.VdcStorageProfile.Name}}, ` + @{N='VCpuInMhz';E={$_.ExtensionData.VCpuInMhz}} | Format-Table -AutoSize + + if ($Configs.OrgVdc.EdgeGateway -like "Yes"){ + Search-Cloud -QueryType EdgeGateway -Name $EdgeName | Select Name, IsBusy, GatewayStatus, HaStatus | ft -AutoSize + } + } + catch { + $Valid = $false + Write-Host "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Creating new OrgVdc Failed" -ForegroundColor Red + } + } + + Write-Output "Overall Execution was Valid: $Valid" + } +} diff --git a/Modules/VMware-vCD-Module/functions/New-MyEdgeGateway.psm1 b/Modules/VMware-vCD-Module/functions/New-MyEdgeGateway.psm1 new file mode 100644 index 0000000..dc8d8f8 --- /dev/null +++ b/Modules/VMware-vCD-Module/functions/New-MyEdgeGateway.psm1 @@ -0,0 +1,165 @@ +Function New-MyEdgeGateway { +<# +.SYNOPSIS + Creates a new Edge Gateway with Default Parameters + +.DESCRIPTION + Creates a new Edge Gateway with Default Parameters + + Default Parameters are: + * HA State + * DNS Relay + + +.NOTES + File Name : New-MyEdgeGateway.ps1 + Author : Markus Kraus + Version : 1.1 + State : Ready + +.LINK + https://mycloudrevolution.com/ + +.EXAMPLE + New-MyEdgeGateway -Name "TestEdge" -OrgVDCName "TestVDC" -OrgName "TestOrg" -Size compact -ExternalNetwork "ExternalNetwork" -IPAddress "192.168.100.1" -SubnetMask "255.255.255.0" -Gateway "192.168.100.254" -IPRangeStart ""192.168.100.2" -IPRangeEnd ""192.168.100.3" -Verbose + +.PARAMETER Name + Name of the New Edge Gateway as String + +.PARAMETER OrgVDCName + OrgVDC where the new Edge Gateway should be created as string + +.PARAMETER OrgName + Org where the new Edge Gateway should be created as string + +.PARAMETER Size + Size of the new Edge Gateway as string + +.PARAMETER ExternalNetwork + External Network of the new Edge Gateway as String + +.PARAMETER IPAddress + IP Address of the New Edge Gateway as IP Address + +.PARAMETER SubnetMask + Subnet Mask of the New Edge Gateway as IP Address + +.PARAMETER Gateway + Gateway of the New Edge Gateway as IP Address + +.PARAMETER IPRangeStart + Sub Allocation IP Range Start of the New Edge Gateway as IP Address + +.PARAMETER IPRangeEnd + Sub Allocation IP Range End of the New Edge Gateway as IP Address + +.PARAMETER Timeout + Timeout for the Edge Gateway to get Ready + + Default: 120s + +#> + Param ( + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Name of the New Edge Gateway as String")] + [ValidateNotNullorEmpty()] + [String] $Name, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="OrgVDC where the new Edge Gateway should be created as string")] + [ValidateNotNullorEmpty()] + [String] $OrgVdcName, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Org where the new Edge Gateway should be created as string")] + [ValidateNotNullorEmpty()] + [String] $OrgName, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Size of the new Edge Gateway as string")] + [ValidateNotNullorEmpty()] + [ValidateSet("compact","full")] + [String] $Size, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="External Network of the New Edge Gateway as String")] + [ValidateNotNullorEmpty()] + [String] $ExternalNetwork, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="IP Address of the New Edge Gateway as IP Address")] + [ValidateNotNullorEmpty()] + [IPAddress] $IPAddress, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Subnet Mask of the New Edge Gateway as IP Address")] + [ValidateNotNullorEmpty()] + [IPAddress] $SubnetMask, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Gateway of the New Edge Gateway as IP Address")] + [ValidateNotNullorEmpty()] + [IPAddress] $Gateway, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Sub Allocation IP Range Start the New Edge Gateway as IP Address")] + [ValidateNotNullorEmpty()] + [IPAddress] $IPRangeStart, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Sub Allocation IP Range End the New Edge Gateway as IP Address")] + [ValidateNotNullorEmpty()] + [IPAddress] $IPRangeEnd, + [Parameter(Mandatory=$False, ValueFromPipeline=$False,HelpMessage="Timeout for the Edge Gateway to get Ready")] + [ValidateNotNullorEmpty()] + [int] $Timeout = 120 + ) + Process { + + ## Get Org vDC + Write-Verbose "Get Org vDC" + [Array] $orgVdc = Get-Org -Name $OrgName | Get-OrgVdc -Name $OrgVdcName + + if ( $orgVdc.Count -gt 1) { + throw "Multiple OrgVdcs found!" + } + elseif ( $orgVdc.Count -lt 1) { + throw "No OrgVdc found!" + } + ## Get External Network + Write-Verbose "Get External Network" + $extNetwork = Get-ExternalNetwork | Get-CIView -Verbose:$False | Where-Object {$_.name -eq $ExternalNetwork} + + ## Build EdgeGatway Configuration + Write-Verbose "Build EdgeGatway Configuration" + $EdgeGateway = New-Object VMware.VimAutomation.Cloud.Views.Gateway + $EdgeGateway.Name = $Name + $EdgeGateway.Configuration = New-Object VMware.VimAutomation.Cloud.Views.GatewayConfiguration + #$EdgeGateway.Configuration.BackwardCompatibilityMode = $false + $EdgeGateway.Configuration.GatewayBackingConfig = $Size + $EdgeGateway.Configuration.UseDefaultRouteForDnsRelay = $false + $EdgeGateway.Configuration.HaEnabled = $false + + $EdgeGateway.Configuration.EdgeGatewayServiceConfiguration = New-Object VMware.VimAutomation.Cloud.Views.GatewayFeatures + $EdgeGateway.Configuration.GatewayInterfaces = New-Object VMware.VimAutomation.Cloud.Views.GatewayInterfaces + + $EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface = New-Object VMware.VimAutomation.Cloud.Views.GatewayInterface + $EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].name = $extNetwork.Name + $EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].DisplayName = $extNetwork.Name + $EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].Network = $extNetwork.Href + $EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].InterfaceType = "uplink" + $EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].UseForDefaultRoute = $true + $EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].ApplyRateLimit = $false + + $ExNetexternalSubnet = New-Object VMware.VimAutomation.Cloud.Views.SubnetParticipation + $ExNetexternalSubnet.Gateway = $Gateway.IPAddressToString + $ExNetexternalSubnet.Netmask = $SubnetMask.IPAddressToString + $ExNetexternalSubnet.IpAddress = $IPAddress.IPAddressToString + $ExNetexternalSubnet.IpRanges = New-Object VMware.VimAutomation.Cloud.Views.IpRanges + $ExNetexternalSubnet.IpRanges.IpRange = New-Object VMware.VimAutomation.Cloud.Views.IpRange + $ExNetexternalSubnet.IpRanges.IpRange[0].StartAddress = $IPRangeStart.IPAddressToString + $ExNetexternalSubnet.IpRanges.IpRange[0].EndAddress = $IPRangeEnd.IPAddressToString + + $EdgeGateway.Configuration.GatewayInterfaces.GatewayInterface[0].SubnetParticipation = $ExNetexternalSubnet + + ## Create EdgeGatway + Write-Verbose "Create EdgeGatway" + $CreateEdgeGateway = $orgVdc.ExtensionData.CreateEdgeGateway($EdgeGateway) + + ## Wait for EdgeGatway to become Ready + Write-Verbose "Wait for EdgeGatway to become Ready" + while((Search-Cloud -QueryType EdgeGateway -Name $Name -Verbose:$False).IsBusy -eq $True){ + $i++ + Start-Sleep 5 + if($i -gt $Timeout) { Write-Error "Creating Edge Gateway."; break} + Write-Progress -Activity "Creating Edge Gateway" -Status "Wait for Edge to become Ready..." + } + Write-Progress -Activity "Creating Edge Gateway" -Completed + Start-Sleep 1 + + Search-Cloud -QueryType EdgeGateway -Name $Name | Select-Object Name, IsBusy, GatewayStatus, HaStatus | Format-Table -AutoSize + + + } +} diff --git a/Modules/VMware-vCD-Module/functions/New-MyOrg.psm1 b/Modules/VMware-vCD-Module/functions/New-MyOrg.psm1 new file mode 100644 index 0000000..6d995e4 --- /dev/null +++ b/Modules/VMware-vCD-Module/functions/New-MyOrg.psm1 @@ -0,0 +1,105 @@ +Function New-MyOrg { +<# +.SYNOPSIS + Creates a new vCD Org with Default Parameters + +.DESCRIPTION + Creates a new vCD Org with Default Parameters. + + Default Parameters are: + * Catalog Publishing + * Catalog Subscription + * VM Quota + * Stored VM Quota + * VM Lease Time + * Stored VM Lease Time + * Password Policy Settings + +.NOTES + File Name : New-MyOrg.ps1 + Author : Markus Kraus + Version : 1.1 + State : Ready + +.LINK + https://mycloudrevolution.com/ + +.EXAMPLE + New-MyOrg -Name "TestOrg" -FullName "Test Org" -Description "PowerCLI Test Org" + +.PARAMETER Name + Name of the New Org as String + +.PARAMETER FullName + Full Name of the New Org as String + +.PARAMETER Description + Description of the New Org as String + +.PARAMETER Enabled + Should the New Org be enabled after creation + + Default:$false + +#> + Param ( + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Name of the New Org as string")] + [ValidateNotNullorEmpty()] + [String] $Name, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Full Name of the New Org as string")] + [ValidateNotNullorEmpty()] + [String] $FullName, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="Description of the New Org as string")] + [ValidateNotNullorEmpty()] + [String] $Description, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="Should the New Org be enabled after creation")] + [ValidateNotNullorEmpty()] + [Switch]$Enabled + ) + Process { + $vcloud = $DefaultCIServers[0].ExtensionData + + ## Create Objects + $AdminOrg = New-Object VMware.VimAutomation.Cloud.Views.AdminOrg + $orgGeneralSettings = New-Object VMware.VimAutomation.Cloud.Views.OrgGeneralSettings + $orgOrgLeaseSettings = New-Object VMware.VimAutomation.Cloud.Views.OrgLeaseSettings + $orgOrgVAppTemplateLeaseSettings = New-Object VMware.VimAutomation.Cloud.Views.OrgVAppTemplateLeaseSettings + $orgOrgPasswordPolicySettings = New-Object VMware.VimAutomation.Cloud.Views.OrgPasswordPolicySettings + $orgSettings = New-Object VMware.VimAutomation.Cloud.Views.OrgSettings + + ## Admin Settings + $adminOrg.Name = $name + $adminOrg.FullName = $FullName + $adminOrg.Description = $description + $adminOrg.IsEnabled = $Enabled + + ## Org Setting + ### General Org Settings + $orgGeneralSettings.CanPublishCatalogs = $False + $orgGeneralSettings.CanPublishExternally = $False + $orgGeneralSettings.CanSubscribe = $True + $orgGeneralSettings.DeployedVMQuota = 0 + $orgGeneralSettings.StoredVmQuota = 0 + $orgSettings.OrgGeneralSettings = $orgGeneralSettings + ### vApp Org Setting + $orgOrgLeaseSettings.DeleteOnStorageLeaseExpiration = $false + $orgOrgLeaseSettings.DeploymentLeaseSeconds = 0 + $orgOrgLeaseSettings.StorageLeaseSeconds = 0 + $orgSettings.VAppLeaseSettings = $orgOrgLeaseSettings + ### vApp Template Org Setting + $orgOrgVAppTemplateLeaseSettings.DeleteOnStorageLeaseExpiration = $false + $orgOrgVAppTemplateLeaseSettings.StorageLeaseSeconds = 0 + $orgSettings.VAppTemplateLeaseSettings = $orgOrgVAppTemplateLeaseSettings + ### PasswordPolicySettings Org Setting + $orgOrgPasswordPolicySettings.AccountLockoutEnabled = $True + $orgOrgPasswordPolicySettings.InvalidLoginsBeforeLockout = 5 + $orgOrgPasswordPolicySettings.AccountLockoutIntervalMinutes = 30 + $orgSettings.OrgPasswordPolicySettings = $orgOrgPasswordPolicySettings + + $adminOrg.Settings = $orgSettings + + $CreateOrg = $vcloud.CreateOrg($adminOrg) + + Get-Org -Name $name | Format-Table -AutoSize + } +} diff --git a/Modules/VMware-vCD-Module/functions/New-MyOrgAdmin.psm1 b/Modules/VMware-vCD-Module/functions/New-MyOrgAdmin.psm1 new file mode 100644 index 0000000..227d30e --- /dev/null +++ b/Modules/VMware-vCD-Module/functions/New-MyOrgAdmin.psm1 @@ -0,0 +1,89 @@ +Function New-MyOrgAdmin { +<# +.SYNOPSIS + Creates a new vCD Org Admin with Default Parameters + +.DESCRIPTION + Creates a new vCD Org Admin with Default Parameters + + Default Parameters are: + * User Role + +.NOTES + File Name : New-MyOrgAdmin.ps1 + Author : Markus Kraus + Version : 1.1 + State : Ready + +.LINK + https://mycloudrevolution.com/ + +.EXAMPLE + New-MyOrgAdmin -Name "OrgAdmin" -Pasword "Anfang!!" -FullName "Org Admin" -EmailAddress "OrgAdmin@TestOrg.local" -Org "TestOrg" + +.PARAMETER Name + Name of the New Org Admin as String + +.PARAMETER FullName + Full Name of the New Org Admin as String + +.PARAMETER Password + Password of the New Org Admin as String + +.PARAMETER EmailAddress + EmailAddress of the New Org Admin as String + +.PARAMETER Enabled + Should the New Org be enabled after creation + + Default:$false + +.PARAMETER Org + Org where the new Org Admin should be created as string + +#> + Param ( + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Name of the New Org Admin as String")] + [ValidateNotNullorEmpty()] + [String] $Name, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Password of the New Org Admin as String")] + [ValidateNotNullorEmpty()] + [String] $Pasword, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Full Name of the New Org Admin as String")] + [ValidateNotNullorEmpty()] + [String] $FullName, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="EmailAddress of the New Org Admin as String")] + [ValidateNotNullorEmpty()] + [String] $EmailAddress, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Org where the new Org Admin should be created as string")] + [ValidateNotNullorEmpty()] + [String] $Org, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="Should the New Org be enabled after creation")] + [ValidateNotNullorEmpty()] + [Switch]$Enabled + ) + Process { + + ## Create Objects + $OrgED = (Get-Org $Org).ExtensionData + $orgAdminUser = New-Object VMware.VimAutomation.Cloud.Views.User + + ## Settings + $orgAdminUser.Name = $Name + $orgAdminUser.FullName = $FullName + $orgAdminUser.EmailAddress = $EmailAddress + $orgAdminUser.Password = $Pasword + $orgAdminUser.IsEnabled = $Enabled + + $vcloud = $DefaultCIServers[0].ExtensionData + + ## Find Role + $orgAdminRole = $vcloud.RoleReferences.RoleReference | Where-Object {$_.Name -eq "Organization Administrator"} + $orgAdminUser.Role = $orgAdminRole + + ## Create User + $user = $orgED.CreateUser($orgAdminUser) + + Get-CIUser -Org $Org -Name $Name | Format-Table -AutoSize + } +} diff --git a/Modules/VMware-vCD-Module/functions/New-MyOrgNetwork.psm1 b/Modules/VMware-vCD-Module/functions/New-MyOrgNetwork.psm1 new file mode 100644 index 0000000..2d7bf5a --- /dev/null +++ b/Modules/VMware-vCD-Module/functions/New-MyOrgNetwork.psm1 @@ -0,0 +1,166 @@ +Function New-MyOrgNetwork { + <# + .SYNOPSIS + Creates a new Org Network with Default Parameters + + .DESCRIPTION + + .NOTES + File Name : New-MyOrgNetwork.ps1 + Author : Markus Kraus + Version : 1.1 + State : Ready + + .LINK + https://mycloudrevolution.com + + .EXAMPLE + New-MyOrgNetwork -Name Test -OrgVdcName "Test-OrgVDC" -OrgName "Test-Org" -EdgeName "Test-OrgEdge" -SubnetMask 255.255.255.0 -Gateway 192.168.66.1 -IPRangeStart 192.168.66.100 -IPRangeEnd 192.168.66.200 + + .EXAMPLE + New-MyOrgNetwork -Name Test -OrgVdcName "Test-OrgVDC" -OrgName "Test-Org" -EdgeName "Test-OrgEdge" -SubnetMask 255.255.255.0 -Gateway 192.168.66.1 -IPRangeStart 192.168.66.100 -IPRangeEnd 192.168.66.200 -Shared:$False + + .EXAMPLE + $params = @{ 'Name' = 'Test'; + 'OrgVdcName'= 'Test-OrgVDC'; + 'OrgName'='Test-Org'; + 'EdgeName'='Test-OrgEdge'; + 'SubnetMask' = '255.255.255.0'; + 'Gateway' = '192.168.66.1'; + 'IPRangeStart' = '192.168.66.100'; + 'IPRangeEnd' = '192.168.66.200' + } + New-MyOrgNetwork @params -Verbose + + .PARAMETER Name + Name of the New Org Network as String + + .PARAMETER OrgVDCName + OrgVDC where the new Org Network should be created as string + + .PARAMETER OrgName + Org where the newOrg Networkshould be created as string + + .PARAMETER EdgeName + Edge Gateway Name for the new Org Network as String + + .PARAMETER SubnetMask + Subnet Mask of the New Org Network as IP Address + + .PARAMETER Gateway + Gateway of the New Org Network as IP Address + + .PARAMETER IPRangeStart + IP Range Start of the New Org Network as IP Address + + .PARAMETER IPRangeEnd + IP Range End of the New Org Network as IP Address + + .PARAMETER Shared + Switch for Shared OrgVDC Network + + Default: $True + + .PARAMETER Timeout + Timeout for the Org Network to become Ready + + Default: 120s + + #> + Param ( + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Name of the New Org Network as String")] + [ValidateNotNullorEmpty()] + [String] $Name, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="OrgVDC where the new Org Network should be created as string")] + [ValidateNotNullorEmpty()] + [String] $OrgVdcName, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Org where the new Org Network should be created as string")] + [ValidateNotNullorEmpty()] + [String] $OrgName, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Edge Gateway Name for the new Org Network as String")] + [ValidateNotNullorEmpty()] + [String] $EdgeName, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Subnet Mask of the New Org Network as IP Address")] + [ValidateNotNullorEmpty()] + [IPAddress] $SubnetMask, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Gateway of the New Org Network as IP Address")] + [ValidateNotNullorEmpty()] + [IPAddress] $Gateway, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="IP Range Start the New Org Network as IP Address")] + [ValidateNotNullorEmpty()] + [IPAddress] $IPRangeStart, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="IP Range End the New Org Network as IP Address")] + [ValidateNotNullorEmpty()] + [IPAddress] $IPRangeEnd, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="Switch for Shared OrgVDC Network")] + [ValidateNotNullorEmpty()] + [Bool] $Shared = $True, + [Parameter(Mandatory=$False, ValueFromPipeline=$False,HelpMessage="Timeout for the Org Network to become Ready")] + [ValidateNotNullorEmpty()] + [int] $Timeout = 120 + ) + Process { + + ## Get Org vDC + Write-Verbose "Get Org vDC" + [Array] $orgVdc = Get-Org -Name $OrgName | Get-OrgVdc -Name $OrgVdcName + + if ( $orgVdc.Count -gt 1) { + throw "Multiple OrgVdcs found!" + } + elseif ( $orgVdc.Count -lt 1) { + throw "No OrgVdc found!" + } + $orgVdcView = $orgVdc| Get-CIView + + ## Get EdgeGateway + Write-Verbose "Get EdgeGateway" + [Array] $edgeGateway = Search-Cloud -QueryType EdgeGateway -Name $EdgeName | Get-CIView + if ( $edgeGateway.Count -gt 1) { + throw "Multiple EdgeGateways found!" + } + elseif ( $edgeGateway.Count -lt 1) { + throw "No EdgeGateway found!" + } + + ## Define Org Network + Write-Verbose "Define Org Network" + $OrgNetwork = new-object vmware.vimautomation.cloud.views.orgvdcnetwork + $OrgNetwork.name = $Name + $OrgNetwork.edgegateway = $edgeGateway.id + $OrgNetwork.isshared = $Shared + + $OrgNetwork.configuration = new-object vmware.vimautomation.cloud.views.networkconfiguration + $OrgNetwork.configuration.fencemode = "natRouted" + $OrgNetwork.configuration.ipscopes = new-object vmware.vimautomation.cloud.views.ipscopes + + $Scope = new-object vmware.vimautomation.cloud.views.ipScope + $Scope.gateway = $Gateway + $Scope.netmask = $SubnetMask + + $Scope.ipranges = new-object vmware.vimautomation.cloud.views.ipranges + $Scope.ipranges.iprange = new-object vmware.vimautomation.cloud.views.iprange + $Scope.ipranges.iprange[0].startaddress = $IPRangeStart + $Scope.ipranges.iprange[0].endaddress = $IPRangeEnd + + $OrgNetwork.configuration.ipscopes.ipscope += $Scope + + ## Create Org Network + Write-Verbose "Create Org Network" + $CreateOrgNetwork = $orgVdcView.CreateNetwork($OrgNetwork) + + ## Wait for Org Network to become Ready + Write-Verbose "Wait for Org Network to become Ready" + while(!(Get-OrgVdcNetwork -Id $CreateOrgNetwork.Id -ErrorAction SilentlyContinue)){ + $i++ + Start-Sleep 5 + if($i -gt $Timeout) { Write-Error "Creating Org Network."; break} + Write-Progress -Activity "Creating Org Network" -Status "Wait for Network to become Ready..." + } + Write-Progress -Activity "Creating Org Network" -Completed + Start-Sleep 1 + + Get-OrgVdcNetwork -Id $CreateOrgNetwork.Id | Select-Object Name, OrgVdc, NetworkType, DefaultGateway, Netmask, StaticIPPool, @{ N='isShared'; E = {$_.ExtensionData.isShared} } | Format-Table -AutoSize + + } + } diff --git a/Modules/VMware-vCD-Module/functions/New-MyOrgVdc.psm1 b/Modules/VMware-vCD-Module/functions/New-MyOrgVdc.psm1 new file mode 100644 index 0000000..90e4cd0 --- /dev/null +++ b/Modules/VMware-vCD-Module/functions/New-MyOrgVdc.psm1 @@ -0,0 +1,265 @@ +Function New-MyOrgVdc { +<# +.SYNOPSIS + Creates a new vCD Org VDC with Default Parameters + +.DESCRIPTION + Creates a new vCD Org VDC with Default Parameters + + Default Parameters are: + * Network Quota + * VM Quota + * 'vCpu In Mhz' + * Fast Provisioning + * Thin Provisioning + * private Catalog + +.NOTES + File Name : New-MyOrgVdc.ps1 + Author : Markus Kraus + Version : 1.3 + State : Ready + +.LINK + https://mycloudrevolution.com/ + +.EXAMPLE + New-MyOrgVdc -Name "TestVdc" -AllocationModel AllocationPool -CPULimit 1000 -MEMLimit 1000 -StorageLimit 1000 -StorageProfile "Standard-DC01" -NetworkPool "NetworkPool-DC01" -ProviderVDC "Provider-VDC-DC01" -Org "TestOrg" -ExternalNetwork "External_OrgVdcNet" + +.EXAMPLE + New-MyOrgVdc -Name "TestVdc" -AllocationModel AllocationVApp -StorageLimit 1000 -StorageProfile "Standard-DC01" -NetworkPool "NetworkPool-DC01" -ProviderVDC "Provider-VDC-DC01" -Org "TestOrg" + +.PARAMETER Name + Name of the New Org VDC as String + +.PARAMETER AllocationModel + Allocation Model of the New Org VDC as String + +.PARAMETER CPULimit + CPU Limit (MHz) of the New Org VDC as String + + Default: 0 (Unlimited) + + Note: If AllocationModel is not AllocationVApp (Pay as you go), a limit needs to be set + +.PARAMETER MEMLimit + Memory Limit (MB) of the New Org VDC as String + + Default: 0 (Unlimited) + + Note: If AllocationModel is not AllocationVApp (Pay as you go), a limit needs to be set + +.PARAMETER StorageLimit + Storage Limit (MB) of the New Org VDC as String + +.PARAMETER StorageProfile + Storage Profile of the New Org VDC as String + +.PARAMETER NetworkPool + Network Pool of the New Org VDC as String + +.PARAMETER ExternalNetwork + Optional External Network of the New Org VDC as String + +.PARAMETER Enabled + Should the New Org VDC be enabled after creation + + Default:$false + + Note: If an External Network is requested the Org VDC will be enabled during External Network Configuration + +.PARAMETER ProviderVDC + ProviderVDC where the new Org VDC should be created as string + +.PARAMETER Org + Org where the new Org VDC should be created as string + +.PARAMETER Timeout + Timeout for the Org VDC to get Ready + + Default: 120s + +#> + Param ( + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Name of the New Org VDC as String")] + [ValidateNotNullorEmpty()] + [String] $Name, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Allocation Model of the New Org VDC as String")] + [ValidateNotNullorEmpty()] + [ValidateSet("AllocationPool","AllocationVApp")] + [String] $AllocationModel, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="CPU Limit (MHz) of the New Org VDC as String")] + [ValidateNotNullorEmpty()] + [int] $CPULimit = 0, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="Memory Limit (MB) of the New Org VDC as String")] + [ValidateNotNullorEmpty()] + [int] $MEMLimit = 0, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Storage Limit (MB) of the New Org VDC as String")] + [ValidateNotNullorEmpty()] + [int] $StorageLimit, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Storage Profile of the New Org VDC as String")] + [ValidateNotNullorEmpty()] + [String] $StorageProfile, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Network Pool of the New Org VDC as String")] + [ValidateNotNullorEmpty()] + [String] $NetworkPool, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="Optional External Network of the New Org VDC as String")] + [ValidateNotNullorEmpty()] + [String] $ExternalNetwork, + [Parameter(Mandatory=$False, ValueFromPipeline=$False, HelpMessage="Should the New Org VDC be enabled after creation")] + [ValidateNotNullorEmpty()] + [Switch]$Enabled, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="ProviderVDC where the new Org VDC should be created as string")] + [ValidateNotNullorEmpty()] + [String] $ProviderVDC, + [Parameter(Mandatory=$True, ValueFromPipeline=$False, HelpMessage="Org where the new Org VDC should be created as string")] + [ValidateNotNullorEmpty()] + [String] $Org, + [Parameter(Mandatory=$False, ValueFromPipeline=$False,HelpMessage="Timeout for the Org VDC to get Ready")] + [ValidateNotNullorEmpty()] + [int] $Timeout = 120 + ) + Process { + ## Create Objects and all Settings + Write-Verbose "Create Objects and all Settings" + $adminVdc = New-Object VMware.VimAutomation.Cloud.Views.AdminVdc + $adminVdc.Name = $name + $adminVdc.IsEnabled = $Enabled + $OrgVdcproviderVdc = Get-ProviderVdc $ProviderVDC + $providerVdcRef = New-Object VMware.VimAutomation.Cloud.Views.Reference + $providerVdcRef.Href = $OrgVdcproviderVdc.Href + $adminVdc.ProviderVdcReference = $providerVdcRef + $adminVdc.AllocationModel = $AllocationModel + $adminVdc.ComputeCapacity = New-Object VMware.VimAutomation.Cloud.Views.ComputeCapacity + $adminVdc.ComputeCapacity.Cpu = New-Object VMware.VimAutomation.Cloud.Views.CapacityWithUsage + $adminVdc.ComputeCapacity.Cpu.Units = "MHz" + $adminVdc.ComputeCapacity.Cpu.Limit = $CPULimit + $adminVdc.ComputeCapacity.Cpu.Allocated = $CPULimit + $adminVdc.ComputeCapacity.Memory = New-Object VMware.VimAutomation.Cloud.Views.CapacityWithUsage + $adminVdc.ComputeCapacity.Memory.Units = "MB" + $adminVdc.ComputeCapacity.Memory.Limit = $MEMLimit + $adminVdc.ComputeCapacity.Memory.Allocated = $MEMLimit + $adminVdc.StorageCapacity = New-Object VMware.VimAutomation.Cloud.Views.CapacityWithUsage + $adminVdc.StorageCapacity.Units = "MB" + $adminVdc.StorageCapacity.Limit = $StorageLimit + $adminVdc.NetworkQuota = 10 + $adminVdc.VmQuota = 0 + $adminVdc.VCpuInMhz = 2000 + $adminVdc.VCpuInMhz2 = 2000 + $adminVdc.UsesFastProvisioning = $false + $adminVdc.IsThinProvision = $true + + ## Create Org vDC + Write-Verbose "Create Org vDC" + $OrgED = (Get-Org $Org).ExtensionData + $orgVdc = $orgED.CreateVdc($adminVdc) + + ## Wait for getting Ready + Write-Verbose "Wait for OrgVdc getting Ready after creation" + $i = 0 + while(($orgVdc = Get-OrgVdc -Name $Name -Verbose:$false).Status -eq "NotReady"){ + $i++ + Start-Sleep 2 + if($i -gt $Timeout) { Write-Error "Creating OrgVdc Failed."; break} + Write-Progress -Activity "Creating OrgVdc" -Status "Wait for OrgVdc to become Ready..." + } + Write-Progress -Activity "Creating OrgVdc" -Completed + Start-Sleep 2 + + ## Search given Storage Profile + Write-Verbose "Search given Storage Profile" + $Filter = "ProviderVdc==" + $OrgVdcproviderVdc.Id + $ProVdcStorageProfile = search-cloud -QueryType ProviderVdcStorageProfile -Name $StorageProfile -Filter $Filter | Get-CIView + + ## Create Storage Profile Object with Settings + Write-Verbose "Create Storage Profile Object with Settings" + $spParams = new-object VMware.VimAutomation.Cloud.Views.VdcStorageProfileParams + $spParams.Limit = $StorageLimit + $spParams.Units = "MB" + $spParams.ProviderVdcStorageProfile = $ProVdcStorageProfile.href + $spParams.Enabled = $true + $spParams.Default = $true + $UpdateParams = new-object VMware.VimAutomation.Cloud.Views.UpdateVdcStorageProfiles + $UpdateParams.AddStorageProfile = $spParams + + ## Update Org vDC + $orgVdc = Get-OrgVdc -Name $name + $orgVdc.ExtensionData.CreateVdcStorageProfile($UpdateParams) + + ## Wait for getting Ready + Write-Verbose "Wait for OrgVdc getting Ready after update" + while(($orgVdc = Get-OrgVdc -Name $name -Verbose:$false).Status -eq "NotReady"){ + $i++ + Start-Sleep 1 + if($i -gt $Timeout) { Write-Error "Update OrgVdc Failed."; break} + Write-Progress -Activity "Updating OrgVdc" -Status "Wait for OrgVdc to become Ready..." + } + Write-Progress -Activity "Updating OrgVdc" -Completed + Start-Sleep 1 + + ## Search Any-StorageProfile + Write-Verbose "Search Any-StorageProfile" + $orgvDCAnyProfile = search-cloud -querytype AdminOrgVdcStorageProfile | Where-Object {($_.Name -match '\*') -and ($_.VdcName -eq $orgVdc.Name)} | Get-CIView + + ## Disable Any-StorageProfile + Write-Verbose "Disable Any-StorageProfile" + $orgvDCAnyProfile.Enabled = $False + $return = $orgvDCAnyProfile.UpdateServerData() + + ## Remove Any-StorageProfile + Write-Verbose "Remove Any-StorageProfile" + $ProfileUpdateParams = new-object VMware.VimAutomation.Cloud.Views.UpdateVdcStorageProfiles + $ProfileUpdateParams.RemoveStorageProfile = $orgvDCAnyProfile.href + $remove = $orgvdc.extensiondata.CreatevDCStorageProfile($ProfileUpdateParams) + + ## Wait for getting Ready + Write-Verbose "Wait for getting Ready" + while(($orgVdc = Get-OrgVdc -Name $name -Verbose:$false).Status -eq "NotReady"){ + $i++ + Start-Sleep 1 + if($i -gt $Timeout) { Write-Error "Update Org Failed."; break} + Write-Progress -Activity "Updating Org" -Status "Wait for Org to become Ready..." + } + Write-Progress -Activity "Updating Org" -Completed + Start-Sleep 1 + + ## Set NetworkPool for correct location + Write-Verbose "Set NetworkPool for correct location" + $orgVdc = Get-OrgVdc -Name $name + $ProVdcNetworkPool = Get-NetworkPool -ProviderVdc $ProviderVDC -Name $NetworkPool + $set = Set-OrgVdc -OrgVdc $orgVdc -NetworkPool $ProVdcNetworkPool -NetworkMaxCount "10" + + ## Create private Catalog + Write-Verbose "Create private Catalog Object" + $OrgCatalog = New-Object VMware.VimAutomation.Cloud.Views.AdminCatalog + $OrgCatalog.name = "$Org Private Catalog" + if (!(Get-Org $org | Get-Catalog -Name $OrgCatalog.name -ErrorAction SilentlyContinue)) { + Write-Verbose "Create private Catalog" + $CreateCatalog = (Get-Org $org | Get-CIView).CreateCatalog($OrgCatalog) + $AccessControlRule = New-CIAccessControlRule -Entity $CreateCatalog.name -EveryoneInOrg -AccessLevel ReadWrite -Confirm:$False + } + else { + Write-Output "Catalog '$($OrgCatalog.name)' aleady exists!" + } + + ## Create a direct connect network + if ($ExternalNetwork) { + Write-Verbose "Create a direct connect network" + Write-Output "Org VDC '$Name' needs to be enabled to add an external Network!" + $EnableOrgVdc = Set-OrgVdc -OrgVdc $Name -Enabled:$True + $orgVdcView = Get-OrgVdc $Name | Get-CIView + $extNetwork = $_.externalnetwork + $extNetwork = Get-ExternalNetwork | Get-CIView -Verbose:$false | Where-Object {$_.name -eq $ExternalNetwork} + $orgNetwork = new-object vmware.vimautomation.cloud.views.orgvdcnetwork + $orgNetwork.name = $ExternalNetwork + $orgNetwork.Configuration = New-Object VMware.VimAutomation.Cloud.Views.NetworkConfiguration + $orgNetwork.Configuration.FenceMode = 'bridged' + $orgNetwork.configuration.ParentNetwork = New-Object vmware.vimautomation.cloud.views.reference + $orgNetwork.configuration.ParentNetwork.href = $extNetwork.href + + $result = $orgVdcView.CreateNetwork($orgNetwork) + } + + Get-OrgVdc -Name $name | Format-Table -AutoSize + } +} diff --git a/Modules/VMware-vCD-Module/media/Invoke-MyOnBoarding.png b/Modules/VMware-vCD-Module/media/Invoke-MyOnBoarding.png new file mode 100644 index 0000000..c09f0fe Binary files /dev/null and b/Modules/VMware-vCD-Module/media/Invoke-MyOnBoarding.png differ diff --git a/Modules/VMware-vCD-Module/media/VSCode-Pester-Tests.png b/Modules/VMware-vCD-Module/media/VSCode-Pester-Tests.png new file mode 100644 index 0000000..2669d07 Binary files /dev/null and b/Modules/VMware-vCD-Module/media/VSCode-Pester-Tests.png differ diff --git a/Modules/VMware-vCD-Module/tests/VMware-vCD-Module.Tests.ps1 b/Modules/VMware-vCD-Module/tests/VMware-vCD-Module.Tests.ps1 new file mode 100644 index 0000000..ada710e --- /dev/null +++ b/Modules/VMware-vCD-Module/tests/VMware-vCD-Module.Tests.ps1 @@ -0,0 +1,35 @@ +$moduleRoot = Resolve-Path "$PSScriptRoot\.." +$moduleName = "VMware-vCD-Module" +$ConfigFile = "$moduleRoot\examples\OnBoarding.json" + +Describe "General project validation: $moduleName" { + + $scripts = Get-ChildItem $moduleRoot -Include *.ps1, *.psm1, *.psd1 -Recurse + + # TestCases are splatted to the script so we need hashtables + $testCase = $scripts | Foreach-Object {@{file = $_}} + It "Script should be valid powershell" -TestCases $testCase { + param($file) + + $file.fullname | Should Exist + + $contents = Get-Content -Path $file.fullname -ErrorAction Stop + $errors = $null + $null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors) + $errors.Count | Should Be 0 + } + + It "Module '$moduleName' prerequirements are met" { + {Import-Module VMware.VimAutomation.Cloud -Force} | Should Not Throw + } + + It "Module '$moduleName' can import cleanly" { + {Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -force } | Should Not Throw + } + + It "Module '$moduleName' JSON example is valid" { + {Get-Content -Raw -Path $ConfigFile | ConvertFrom-Json} | Should Not Throw + } + + +} diff --git a/Modules/VMware-vCD-TenantReport/README.md b/Modules/VMware-vCD-TenantReport/README.md new file mode 100644 index 0000000..ac9b9f8 --- /dev/null +++ b/Modules/VMware-vCD-TenantReport/README.md @@ -0,0 +1,30 @@ +VMware-vCD-TenantReport PowerShell Module +============= + +# About + +## Project Owner: + +Markus Kraus [@vMarkus_K](https://twitter.com/vMarkus_K) + +MY CLOUD-(R)EVOLUTION [mycloudrevolution.com](http://mycloudrevolution.com/) + +## Project WebSite: + +[mycloudrevolution.com](http://mycloudrevolution.com/) + +## Project Documentation: + +[Read the Docs](http://readthedocs.io/) + +## Project Description: + +The 'VMware-vCD-TenantReport' PowerShell Module creates with the Fuction 'Get-VcdTenantReport' a HTML Report of your vCloud Director Objects. + +![Get-VcdTenantReport](/media/Get-VcdTenantReport.png) + +Big thanks to [Timothy Dewin](https://twitter.com/tdewin) for his great [PowerStartHTML](https://github.com/tdewin/randomsamples/tree/master/powerstarthtml) PowerShell Module which is used to generate the Report for this Module. + + + + diff --git a/Modules/VMware-vCD-TenantReport/VMware-vCD-TenantReport.psd1 b/Modules/VMware-vCD-TenantReport/VMware-vCD-TenantReport.psd1 new file mode 100644 index 0000000..32afbb5 --- /dev/null +++ b/Modules/VMware-vCD-TenantReport/VMware-vCD-TenantReport.psd1 @@ -0,0 +1,122 @@ +# +# Modulmanifest für das Modul "VMware-vCD-TenantReport" +# +# Generiert von: Markus Kraus +# +# Generiert am: 8/22/2017 +# + +@{ + +# Die diesem Manifest zugeordnete Skript- oder Binärmoduldatei. +# RootModule = 'VMware-vCD-TenantReport.psm1' + +# Die Versionsnummer dieses Moduls +ModuleVersion = '1.0.2' + +# ID zur eindeutigen Kennzeichnung dieses Moduls +GUID = '21a71eaa-d259-48c5-8482-643ba152af76' + +# Autor dieses Moduls +Author = 'Markus' + +# Unternehmen oder Hersteller dieses Moduls +CompanyName = 'mycloudrevolution.com' + +# Urheberrechtserklärung für dieses Modul +Copyright = '(c) 2017 Markus Kraus. Alle Rechte vorbehalten.' + +# Beschreibung der von diesem Modul bereitgestellten Funktionen +# Description = '' + +# Die für dieses Modul mindestens erforderliche Version des Windows PowerShell-Moduls +# PowerShellVersion = '' + +# Der Name des für dieses Modul erforderlichen Windows PowerShell-Hosts +# PowerShellHostName = '' + +# Die für dieses Modul mindestens erforderliche Version des Windows PowerShell-Hosts +# PowerShellHostVersion = '' + +# Die für dieses Modul mindestens erforderliche Microsoft .NET Framework-Version +# DotNetFrameworkVersion = '' + +# Die für dieses Modul mindestens erforderliche Version der CLR (Common Language Runtime) +# CLRVersion = '' + +# Die für dieses Modul erforderliche Prozessorarchitektur ("Keine", "X86", "Amd64"). +# ProcessorArchitecture = '' + +# Die Module, die vor dem Importieren dieses Moduls in die globale Umgebung geladen werden müssen +# RequiredModules = @() + +# Die Assemblys, die vor dem Importieren dieses Moduls geladen werden müssen +# RequiredAssemblies = @() + +# Die Skriptdateien (PS1-Dateien), die vor dem Importieren dieses Moduls in der Umgebung des Aufrufers ausgeführt werden. +# ScriptsToProcess = @() + +# Die Typdateien (.ps1xml), die beim Importieren dieses Moduls geladen werden sollen +# TypesToProcess = @() + +# Die Formatdateien (.ps1xml), die beim Importieren dieses Moduls geladen werden sollen +# FormatsToProcess = @() + +# Die Module, die als geschachtelte Module des in "RootModule/ModuleToProcess" angegebenen Moduls importiert werden sollen. +NestedModules = @( "modules/PowerStartHTML/PowerStartHTML.psd1", + "functions/Get-VcdTenantReport.psm1" ) + +# Aus diesem Modul zu exportierende Funktionen +FunctionsToExport = '*' + +# Aus diesem Modul zu exportierende Cmdlets +CmdletsToExport = '*' + +# Die aus diesem Modul zu exportierenden Variablen +VariablesToExport = '*' + +# Aus diesem Modul zu exportierende Aliase +AliasesToExport = '*' + +# Aus diesem Modul zu exportierende DSC-Ressourcen +# DscResourcesToExport = @() + +# Liste aller Module in diesem Modulpaket +# ModuleList = @() + +# Liste aller Dateien in diesem Modulpaket +# FileList = @() + +# Die privaten Daten, die an das in "RootModule/ModuleToProcess" angegebene Modul übergeben werden sollen. Diese können auch eine PSData-Hashtabelle mit zusätzlichen von PowerShell verwendeten Modulmetadaten enthalten. +PrivateData = @{ + + PSData = @{ + + # 'Tags' wurde auf das Modul angewendet und unterstützt die Modulermittlung in Onlinekatalogen. + # Tags = @() + + # Eine URL zur Lizenz für dieses Modul. + # LicenseUri = '' + + # Eine URL zur Hauptwebsite für dieses Projekt. + # ProjectUri = '' + + # Eine URL zu einem Symbol, das das Modul darstellt. + # IconUri = '' + + # 'ReleaseNotes' des Moduls + # ReleaseNotes = '' + + } # Ende der PSData-Hashtabelle + +} # Ende der PrivateData-Hashtabelle + +# HelpInfo-URI dieses Moduls +# HelpInfoURI = '' + +# Standardpräfix für Befehle, die aus diesem Modul exportiert werden. Das Standardpräfix kann mit "Import-Module -Prefix" überschrieben werden. +# DefaultCommandPrefix = '' + +} + + diff --git a/Modules/VMware-vCD-TenantReport/functions/Get-VcdTenantReport.psm1 b/Modules/VMware-vCD-TenantReport/functions/Get-VcdTenantReport.psm1 new file mode 100644 index 0000000..ee1296e --- /dev/null +++ b/Modules/VMware-vCD-TenantReport/functions/Get-VcdTenantReport.psm1 @@ -0,0 +1,251 @@ +function Get-VcdTenantReport { +<# + .NOTES + =========================================================================== + Created by: Markus Kraus + Twitter: @VMarkus_K + Private Blog: mycloudrevolution.com + =========================================================================== + Changelog: + 1.0.0 - Inital Release + 1.0.1 - Removed "Test-IP" Module + 1.0.2 - More Detailed Console Log + =========================================================================== + External Code Sources: + Examle Usage of BOOTSTRAP with PowerShell + https://github.com/tdewin/randomsamples/tree/master/powershell-veeamallstat + BOOTSTRAP with PowerShell + https://github.com/tdewin/randomsamples/tree/master/powerstarthtml + =========================================================================== + Tested Against Environment: + vCD Version: 8.20 + PowerCLI Version: PowerCLI 6.5.1 + PowerShell Version: 5.0 + OS Version: Windows 8.1 + Keyword: VMware, vCD, Report, HTML + =========================================================================== + + .DESCRIPTION + This Function creates a HTML Report for your vCloud Director Organization. + + This Function is fully tested as Organization Administrator. + With lower permissions a unexpected behavior is possible. + + .Example + Get-VcdTenantReport -Server $ServerFQDN -Org $OrgName -Credential $MyCedential + + .Example + Get-VcdTenantReport -Server $ServerFQDN -Org $OrgName -Path "C:\Temp\Report.html" + + .PARAMETER Server + The FQDN of your vCloud Director Endpoint. + + .PARAMETER Org + The Organization Name. + + .PARAMETER Credential + PowerShell Credentials to access the Eénvironment. + + .PARAMETER Path + The Path of the exported HTML Report. + +#> +#Requires -Version 5 +#Requires -Modules VMware.VimAutomation.Cloud, @{ModuleName="VMware.VimAutomation.Cloud";ModuleVersion="6.5.1.0"} + +[CmdletBinding()] +param( + [Parameter(Mandatory=$True, ValueFromPipeline=$False)] + [ValidateNotNullorEmpty()] + [String] $Server, + [Parameter(Mandatory=$True, ValueFromPipeline=$False)] + [ValidateNotNullorEmpty()] + [String] $Org, + [Parameter(Mandatory=$False, ValueFromPipeline=$False)] + [ValidateNotNullorEmpty()] + [PSCredential] $Credential, + [Parameter(Mandatory=$false, ValueFromPipeline=$False)] + [ValidateNotNullorEmpty()] + [String] $Path = ".\Report.html" + +) + +Process { + + # Start Connection to vCD + + if ($global:DefaultCIServers) { + "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - Disconnect existing vCD Server ..." + $Trash = Disconnect-CIServer -Server * -Force:$true -Confirm:$false -ErrorAction SilentlyContinue + } + + "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - Connect vCD Server ..." + if ($Credential) { + $Trash = Connect-CIServer -Server $Server -Org $Org -Credential $Credential -ErrorAction Stop + } + else { + $Trash = Connect-CIServer -Server $Server -Org $Org -ErrorAction Stop + } + "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - Create HTML Report..." + + # Init HTML Report + $ps = New-PowerStartHTML -title "vCD Tenant Report" + + #Set CSS Style + $ps.cssStyles['.bgtitle'] = "background-color:grey" + $ps.cssStyles['.bgsubsection'] = "background-color:#eee;" + + # Processing Data + ## Get Main Objects + [Array] $OrgVdcs = Get-OrgVdc + [Array] $Catalogs = Get-Catalog + [Array] $Users = Get-CIUser + + ## Add Header to Report + $ps.Main().Add("div","jumbotron").N() + $ps.Append("h1","display-3",("vCD Tenant Report" -f $OrgVdcs.Count)).Append("p","lead","Organization User Count: {0}" -f $Users.Count).Append("p","lead","Organization Catalog Count: {0}" -f $Catalogs.Count).Append("p","lead","Organization VDC Count: {0}" -f $OrgVdcs.Count).Append("hr","my-4").Append("p","font-italic","This Report lists the most important objects in your vCD Environmet. For more details contact your Service Provider").N() + + ## add Org Users to Report + $ps.Main().Append("h2",$null,"Org Users").N() + + $ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"User Name").Append("th",$null,"Locked").Append("th",$null,"DeployedVMCount").Append("th",$null,"StoredVMCount").N() + $ps.Add("tr").N() + + foreach ($User in $Users) { + $ps.Append("td",$null,$User.Name).N() + $ps.Append("td",$null,$User.Locked).N() + $ps.Append("td",$null,$User.DeployedVMCount).N() + $ps.Append("td",$null,$User.StoredVMCount).N() + $ps.Up().N() + + } + $ps.Up().N() + + ## add Org Catalogs to Report + $ps.Main().Append("h2",$null,"Org Catalogs").N() + + foreach ($Catalog in $Catalogs) { + $ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"Catalog Name").N() + $ps.Add("tr").N() + $ps.Append("td",$null,$Catalog.Name).Up().N() + + $ps.Add("td","bgsubsection").N() + $ps.Add("table","table bgcolorsub").N() + $ps.Add("tr").N() + + $headers = @("Item") + foreach ($h in $headers) { + $ps.Append("th",$null,$h).N() + } + $ps.Up().N() + + ### add Itens of the Catalog to the Report + [Array] $Items = $Catalog.ExtensionData.CatalogItems.CatalogItem + + foreach ($Item in $Items) { + $ps.Add("tr").N() + $ps.Append("td",$null,$Item.Name).N() + + $ps.Up().N() + + } + + $ps.Up().Up().N() + } + $ps.Up().N() + + ## add Org VDC`s to the Report + $ps.Main().Append("h2",$null,"Org VDCs").N() + + foreach ($OrgVdc in $OrgVdcs) { + $ps.Main().Add('table','table table-striped table-inverse').Add("tr").Append("th",$null,"VDC Name").Append("th",$null,"Enabled").Append("th",$null,"CpuUsedGHz").Append("th",$null,"MemoryUsedGB").Append("th",$null,"StorageUsedGB").Up().N() + $ps.Add("tr").N() + $ps.Append("td",$null,$OrgVdc.Name).Append("td",$null,$OrgVdc.Enabled).Append("td",$null,$OrgVdc.CpuUsedGHz).Append("td",$null,$OrgVdc.MemoryUsedGB).Append("td",$null,[Math]::Round($OrgVdc.StorageUsedGB,2)).Up().N() + + ### add Edge Gateways of this Org VDC to Report + $ps.Main().Append("h3",$null,"Org VDC Edge Gateways").N() + [Array] $Edges = Search-Cloud -QueryType EdgeGateway -Filter "Vdc==$($OrgVdc.Id)" + + foreach ($Edge in $Edges) { + $ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"Edge Name").N() + $ps.Add("tr").N() + $ps.Append("td",$null,$Edge.Name).Up().N() + + $ps.Add("td","bgsubsection").N() + $ps.Add("table","table bgcolorsub").N() + $ps.Append("tr").Append("td","font-weight-bold","HaStatus").Append("td",$null,($Edge.HaStatus)).N() + $ps.Append("td","font-weight-bold","AdvancedNetworkingEnabled").Append("td",$null,$Edge.AdvancedNetworkingEnabled).N() + $ps.Append("tr").Append("td","font-weight-bold","NumberOfExtNetworks").Append("td",$null,($Edge.NumberOfExtNetworks)).N() + $ps.Append("td","font-weight-bold","NumberOfOrgNetworks").Append("td",$null,$Edge.NumberOfOrgNetworks).N() + + $ps.Up().Up().N() + } + $ps.Up().N() + + ### add Org Networks of this Org VDC to Report + $ps.Main().Append("h3",$null,"Org VDC Networks").N() + [Array] $Networks = $OrgVdc | Get-OrgVdcNetwork + + foreach ($Network in $Networks) { + $ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"Network Name").N() + $ps.Add("tr").N() + $ps.Append("td",$null,$Network.Name).Up().N() + + $ps.Add("td","bgsubsection").N() + $ps.Add("table","table bgcolorsub").N() + $ps.Append("tr").Append("td","font-weight-bold","DefaultGateway").Append("td",$null,($Network.DefaultGateway)).N() + $ps.Append("td","font-weight-bold","Netmask").Append("td",$null,$Network.Netmask).N() + $ps.Append("tr").Append("td","font-weight-bold","NetworkType").Append("td",$null,($Network.NetworkType)).N() + $ps.Append("td","font-weight-bold","StaticIPPool").Append("td",$null,$Network.StaticIPPool).N() + + $ps.Up().Up().N() + } + $ps.Up().N() + + ### add vApps of this Org VDC to Report + $ps.Main().Append("h3",$null,"Org VDC vApps").N() + + [Array] $Vapps = $OrgVdc | Get-CIVApp + + foreach ($Vapp in $Vapps) { + $ps.Add('table','table').Add("tr","bgtitle text-white").Append("th",$null,"vApp Name").Append("th",$null,"Owner").Up().N() + $ps.Add("tr").N() + $ps.Append("td",$null,$Vapp.Name).Append("td",$null,$Vapp.Owner).Up().N() + + #### add VMs of this vApp to Report + $ps.Add("td","bgsubsection").N() + $ps.Add("table","table bgcolorsub").N() + $ps.Add("tr").N() + + $headers = @("Name","Status","GuestOSFullName","CpuCount","MemoryGB") + foreach ($h in $headers) { + $ps.Append("th",$null,$h).N() + } + $ps.Up().N() + + [Array] $VMs = $Vapp | Get-CIVM + + foreach ($VM in $VMs) { + $ps.Add("tr").N() + $ps.Append("td",$null,$VM.Name).N() + $ps.Append("td",$null,$VM.Status).N() + $ps.Append("td",$null,$VM.GuestOSFullName).N() + $ps.Append("td",$null,$VM.CpuCount).N() + $ps.Append("td",$null,$VM.MemoryGB).N() + + $ps.Up().N() + + } + $ps.Up().Up().N() + + } + $ps.Up().N() + + } + $ps.save($Path) + + "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") - Open HTML Report..." + Start-Process $Path + +} +} diff --git a/Modules/VMware-vCD-TenantReport/media/Get-VcdTenantReport.png b/Modules/VMware-vCD-TenantReport/media/Get-VcdTenantReport.png new file mode 100644 index 0000000..c42dacf Binary files /dev/null and b/Modules/VMware-vCD-TenantReport/media/Get-VcdTenantReport.png differ diff --git a/Modules/VMware-vCD-TenantReport/modules/PowerStartHTML/PowerStartHTML.psd1 b/Modules/VMware-vCD-TenantReport/modules/PowerStartHTML/PowerStartHTML.psd1 new file mode 100644 index 0000000..4bf4ee8 Binary files /dev/null and b/Modules/VMware-vCD-TenantReport/modules/PowerStartHTML/PowerStartHTML.psd1 differ diff --git a/Modules/VMware-vCD-TenantReport/modules/PowerStartHTML/PowerStartHTML.psm1 b/Modules/VMware-vCD-TenantReport/modules/PowerStartHTML/PowerStartHTML.psm1 new file mode 100644 index 0000000..0004cea --- /dev/null +++ b/Modules/VMware-vCD-TenantReport/modules/PowerStartHTML/PowerStartHTML.psm1 @@ -0,0 +1,237 @@ +class PowerStartHTML { + [string]$PowerStartHtmlTemplate = "
" + [xml]$xmlDocument = $null + $onLoadJS = $null + $cssStyles= @{} + $lastEl = $null + $newEl = $null + $indentedOutput = $false + $bootstrapAtCompile = $false + PowerStartHTML([string]$title) { + $this.xmlDocument = $this.PowerStartHtmlTemplate + $this.xmlDocument.html.head.title = $title + $this.lastEl = $this.xmlDocument.html.body.ChildNodes[0] + $this.onLoadJS = New-Object System.Collections.Generic.List[System.String] + } + [string] GetHtml() { + $xmlclone = $this.xmlDocument.Clone() + $csb = [System.Text.StringBuilder]::new() + foreach ($cssStyle in $this.cssStyles.GetEnumerator()) { + $null = $csb.AppendFormat("{0} {{ {1} }}",$cssStyle.Name,$cssStyle.Value) + } + $this.xmlDocument.html.head.style = $csb.toString() + $this.AddBootStrapAtCompile() + if($this.onLoadJS.Count -gt 0) { + $this.onLoadJs.Insert(0,"`r`n`$(document).ready(function() {") + $this.onLoadJs.Add("})`r`n") + $el = $this.xmlDocument.CreateElement("script") + $el.AppendChild($this.xmlDocument.CreateTextNode([System.String]::Join("`r`n",$this.onLoadJs))) + $this.xmlDocument.html.body.AppendChild($el) + } + $ms = [System.IO.MemoryStream]::new() + $xmlWriter = [System.Xml.XmlTextWriter]::new($ms,[System.Text.Encoding]::UTF8) + if($this.indentedOutput) { + $xmlWriter.Formatting = [System.Xml.Formatting]::Indented + } + $this.xmlDocument.WriteContentTo($xmlWriter) + $xmlWriter.Flush() + $ms.Flush() + #make sure that everytime we do gethtml we keep it clean + $this.xmlDocument = $xmlclone + $ms.Position = 0; + $sr = [System.IO.StreamReader]::new($ms); + return ("{0}`r`n" -f $sr.ReadToEnd()) + } + Save($path) { + $this.GetHtml() | Set-Content -path $path -Encoding UTF8 + } + + AddAttr($el,$name,$value) { + $attr = $this.xmlDocument.CreateAttribute($name) + $attr.Value = $value + $el.Attributes.Append($attr) + } + + AddAttrs($el,$dict) { + foreach($a in $dict.GetEnumerator()) { + $this.AddAttr($el,$a.Name,$a.Value) + } + } + [PowerStartHTML] AddBootStrap() { + $this.bootstrapAtCompile = $true + return $this + } + AddJSScript($href,$integrity) { + $el = $this.xmlDocument.CreateElement("script") + $attrs = @{ + "src"="$href"; + "integrity"="$integrity"; + "crossorigin"="anonymous" + } + $this.AddAttrs($el,$attrs) + $el.AppendChild($this.xmlDocument.CreateTextNode("")) + $this.xmlDocument.html.body.AppendChild($el) + } + AddBootStrapAtCompile() { #Bootstrap script needs to be added at the end + if($this.bootstrapAtCompile) { + $el = $this.xmlDocument.CreateElement("link") + $attrs = @{ + "rel"="stylesheet"; + "href"='https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css'; + "integrity"="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M"; + "crossorigin"="anonymous" + } + $this.AddAttrs($el,$attrs) + $el.AppendChild($this.xmlDocument.CreateTextNode("")) + $this.xmlDocument.html.head.AppendChild($el) + $this.AddJSScript('https://code.jquery.com/jquery-3.2.1.slim.min.js',"sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN") + $this.AddJSScript('https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js',"sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4") + $this.AddJSScript('https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js',"sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1") + } + } + [PowerStartHTML] AddContainerAttrToMain() { + $this.AddAttr($this.xmlDocument.html.body.ChildNodes[0],"class","container") + return $this + } + [PowerStartHTML] Append($elType = "table",$className=$null,[string]$text=$null) { + $el = $this.xmlDocument.CreateElement($elType) + if($text -ne $null) { + $el.AppendChild($this.xmlDocument.CreateTextNode($text)) + } + if($className -ne $null) { + $this.AddAttr($el,"class",$className) + } + $this.lastEl.AppendChild($el) + $this.newEl = $el + + return $this + } + [PowerStartHTML] Append($elType = "table",$className=$null) { return $this.Append($elType,$className,$null) } + [PowerStartHTML] Append($elType = "table") { return $this.Append($elType,$null,$null) } + [PowerStartHTML] Add($elType = "table",$className=$null,[string]$text=$null) { + $this.Append($elType,$className,$text) + $this.lastEl = $this.newEl + return $this + } + [PowerStartHTML] Add($elType = "table",$className=$null) { return $this.Add($elType,$className,$null) } + [PowerStartHTML] Add($elType = "table") { return $this.Add($elType,$null,$null) } + [PowerStartHTML] Main() { + $this.lastEl = $this.xmlDocument.html.body.ChildNodes[0]; + return $this + } + [PowerStartHTML] Up() { + $this.lastEl = $this.lastEl.ParentNode; + return $this + } + N() {} +} +class PowerStartHTMLPassThroughLine { + $object;$cells + PowerStartHTMLPassThroughLine($object) { + $this.object = $object; + $this.cells = new-object System.Collections.HashTable; + } +} +class PowerStartHTMLPassThroughElement { + $name;$text;$element;$id + PowerStartHTMLPassThroughElement($name,$text,$element,$id) { + $this.name = $name; $this.text = $text; $this.element = $element;$this.id = $id + } +} +function New-PowerStartHTML { + param( + [Parameter(Mandatory=$true)][string]$title, + [switch]$nobootstrap=$false + ) + $pshtml = (new-object PowerStartHTML($title)) + if(-not $nobootstrap) { + $pshtml.AddBootStrap().AddContainerAttrToMain().N() + } + return $pshtml +} +function Add-PowerStartHTMLTable { + param( + [Parameter(Mandatory=$True,ValueFromPipeline=$True)]$object, + [PowerStartHTML]$psHtml, + [string]$tableTitle = $null, + [string]$tableClass = $null, + [string]$idOverride = $(if($tableTitle -ne $null) {($tableTitle.toLower() -replace "[^a-z0-9]","-") }), + [switch]$passthroughTable = $false, + [switch]$noheaders = $false + ) + begin { + if($tableTitle -ne $null) { + $psHtml.Main().Append("h1",$null,$tableTitle).N() + if($idOverride -ne $null) { + $psHtml.AddAttr($psHtml.newEl,"id","header-$idOverride") + } + } + $psHtml.Main().Add("table").N() + [int]$r = 0 + [int]$c = 0 + if($idOverride -ne $null) { + $psHtml.AddAttr($psHtml.newEl,"id","table-$idOverride") + } + if($tableClass -ne $null) { + $psHtml.AddAttr($psHtml.newEl,"class",$tableClass) + } + [bool]$isFirst = $true + } + process { + $c = 0 + + $props = $object | Get-Member -Type Properties + if(-not $noheaders -and $isFirst) { + $psHtml.Add("tr").N() + if($idOverride -ne $null) { + $psHtml.AddAttr($psHtml.newEl,"id","table-$idOverride-trh") + } + $props | % { + $n = $_.Name; + $psHtml.Append("th",$null,$n).N() + if($idOverride -ne $null) { + $cellid = "table-$idOverride-td-$r-$c" + $psHtml.AddAttr($psHtml.newEl,"id",$cellid) + } + $c++ + } + $c = 0 + $psHtml.Up().N() + } + + $psHtml.Add("tr").N() + if($idOverride -ne $null) { + $psHtml.AddAttr($psHtml.newEl,"id","table-$idOverride-tr-$r") + } + $pstableln = [PowerStartHTMLPassThroughLine]::new($object) + + $props | % { + $n = $_.Name; + $psHtml.Append("td",$null,$object."$n").N() + $cellid = $null + if($idOverride -ne $null) { + $cellid = "table-$idOverride-td-$r-$c" + $psHtml.AddAttr($psHtml.newEl,"id",$cellid) + } + if($passthroughTable) { + $pstableln.cells.Add($n,[PowerStartHTMLPassThroughElement]::new($n,($object."$n"),$psHtml.newEl,$cellid)) + } + + $c++ + } + if($passthroughTable) { + $pstableln + } + $psHtml.Up().N() + $isFirst = $false + $r++ + } + end { + } +} + + +Export-ModuleMember -Function @('New-PowerStartHTML','Add-PowerStartHTMLTable') + + + diff --git a/Modules/VMware-vCD-TenantReport/tests/VMware-vCD-TenantReport.Tests.ps1 b/Modules/VMware-vCD-TenantReport/tests/VMware-vCD-TenantReport.Tests.ps1 new file mode 100644 index 0000000..2effa50 --- /dev/null +++ b/Modules/VMware-vCD-TenantReport/tests/VMware-vCD-TenantReport.Tests.ps1 @@ -0,0 +1,24 @@ +$moduleRoot = Resolve-Path "$PSScriptRoot\.." +$moduleName = "VMware-vCD-TenantReport" + +Describe "General project validation: $moduleName" { + + $scripts = Get-ChildItem $moduleRoot -Include *.ps1, *.psm1, *.psd1 -Recurse + + # TestCases are splatted to the script so we need hashtables + $testCase = $scripts | Foreach-Object {@{file = $_}} + It "Script should be valid powershell" -TestCases $testCase { + param($file) + + $file.fullname | Should Exist + + $contents = Get-Content -Path $file.fullname -ErrorAction Stop + $errors = $null + $null = [System.Management.Automation.PSParser]::Tokenize($contents, [ref]$errors) + $errors.Count | Should Be 0 + } + + It "Module '$moduleName' can import cleanly" { + {Import-Module (Join-Path $moduleRoot "$moduleName.psd1") -force } | Should Not Throw + } +} diff --git a/Modules/VMware.CSP/VMware.CSP.psm1 b/Modules/VMware.CSP/VMware.CSP.psm1 new file mode 100644 index 0000000..b4b4515 --- /dev/null +++ b/Modules/VMware.CSP/VMware.CSP.psm1 @@ -0,0 +1,54 @@ +Function Get-CSPAccessToken { + <# + .NOTES + =========================================================================== + Created by: William Lam + Date: 07/23/2018 + Organization: VMware + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .DESCRIPTION + Converts a Refresh Token from the VMware Console Services Portal + to CSP Access Token to access CSP API + .PARAMETER RefreshToken + The Refresh Token from the VMware Console Services Portal + .EXAMPLE + Get-CSPAccessToken -RefreshToken $RefreshToken + #> + Param ( + [Parameter(Mandatory=$true)][String]$RefreshToken + ) + + $results = Invoke-WebRequest -Uri "https://console.cloud.vmware.com/csp/gateway/am/api/auth/api-tokens/authorize?refresh_token=$RefreshToken" -Method POST -ContentType "application/json" -UseBasicParsing -Headers @{"csp-auth-token"="$RefreshToken"} + if($results.StatusCode -ne 200) { + Write-Host -ForegroundColor Red "Failed to retrieve Access Token, please ensure your VMC Refresh Token is valid and try again" + break + } + $accessToken = ($results | ConvertFrom-Json).access_token + Write-Host "CSP Auth Token has been successfully retrieved and saved to `$env:cspAuthToken" + $env:cspAuthToken = $accessToken +} + +Function Get-CSPServices { + <# + .NOTES + =========================================================================== + Created by: William Lam + Date: 07/23/2018 + Organization: VMware + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .DESCRIPTION + Returns the list of CSP Services avialable for given user + .EXAMPLE + Get-CSPServices + #> + If (-Not $env:cspAuthToken) { Write-error "CSP Auth Token not found, please run Get-CSPAccessToken" } Else { + $results = Invoke-WebRequest -Uri "https://console.cloud.vmware.com/csp/gateway/slc/api/definitions?expand=1" -Method GET -ContentType "application/json" -UseBasicParsing -Headers @{"csp-auth-token"="$env:cspAuthToken"} + ((($results.Content) | ConvertFrom-Json).results | where {$_.visible -eq $true}).displayName + } +} \ No newline at end of file diff --git a/Modules/VMware.HCX/VMware.HCX.psd1 b/Modules/VMware.HCX/VMware.HCX.psd1 new file mode 100644 index 0000000..a5e74f0 --- /dev/null +++ b/Modules/VMware.HCX/VMware.HCX.psd1 @@ -0,0 +1,88 @@ +# +# Module manifest for module 'VMware.HCX' +# +# Generated by: wlam@vmware.com +# +# Generated on: 09/11/18 +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'VMware.HCX.psm1' + +# Version number of this module. +ModuleVersion = '1.0.2' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = '88898ed6-26e8-4dfa-a9de-10d3a12571de' + +# Author of this module +Author = 'William Lam' + +# Company or vendor of this module +CompanyName = 'VMware' + +# Copyright statement for this module +Copyright = '(c) 2018 VMware. All rights reserved.' + +# Description of the functionality provided by this module +Description = 'PowerShell Module for Managing Hybrid Cloud Extension (HCX) on VMware Cloud on AWS' + +# Minimum version of the Windows PowerShell engine required by this module +PowerShellVersion = '6.0' + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = 'Connect-HcxServer', 'Get-HcxCloudConfig', 'Get-HcxEndpoint', 'New-HcxMigration', 'Get-HcxMigration', 'Connect-HcxVAMI', 'Get-HcxVCConfig', 'Set-HcxLicense', 'Set-HcxVCConfig', 'Get-HcxNSXConfig', 'Set-HcxNSXConfig', 'Get-HcxCity', 'Get-HcxLocation', 'Set-HcxLocation', 'Get-HcxRoleMapping', 'Set-HcxRoleMapping', 'Get-HcxProxy', 'Set-HcxProxy', 'Remove-HcxProxy' +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = @() + +# Variables to export from this module +VariablesToExport = '*' + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = @() + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + # ProjectUri = '' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} \ No newline at end of file diff --git a/Modules/VMware.HCX/VMware.HCX.psm1 b/Modules/VMware.HCX/VMware.HCX.psm1 new file mode 100644 index 0000000..cfbe95b --- /dev/null +++ b/Modules/VMware.HCX/VMware.HCX.psm1 @@ -0,0 +1,1309 @@ +Function Connect-HcxServer { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/16/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Connect to the HCX Enterprise Manager + .DESCRIPTION + This cmdlet connects to the HCX Enterprise Manager + .EXAMPLE + Connect-HcxServer -Server $HCXServer -Username $Username -Password $Password +#> + Param ( + [Parameter(Mandatory=$true)][String]$Server, + [Parameter(Mandatory=$true)][String]$Username, + [Parameter(Mandatory=$true)][String]$Password + ) + + $payload = @{ + "username" = $Username + "password" = $Password + } + $body = $payload | ConvertTo-Json + + $hcxLoginUrl = "https://$Server/hybridity/api/sessions" + + if($PSVersionTable.PSEdition -eq "Core") { + $results = Invoke-WebRequest -Uri $hcxLoginUrl -Body $body -Method POST -UseBasicParsing -ContentType "application/json" -SkipCertificateCheck + } else { + $results = Invoke-WebRequest -Uri $hcxLoginUrl -Body $body -Method POST -UseBasicParsing -ContentType "application/json" + } + + if($results.StatusCode -eq 200) { + $hcxAuthToken = $results.Headers.'x-hm-authorization' + + $headers = @{ + "x-hm-authorization"="$hcxAuthToken" + "Content-Type"="application/json" + "Accept"="application/json" + } + + $global:hcxConnection = new-object PSObject -Property @{ + 'Server' = "https://$server/hybridity/api"; + 'headers' = $headers + } + $global:hcxConnection + } else { + Write-Error "Failed to connect to HCX Manager, please verify your vSphere SSO credentials" + } +} + +Function Get-HcxCloudConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/16/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Returns the Cloud HCX information that is registerd with HCX Manager + .DESCRIPTION + This cmdlet returns the Cloud HCX information that is registerd with HCX Manager + .EXAMPLE + Get-HcxCloudConfig +#> + If (-Not $global:hcxConnection) { Write-error "HCX Auth Token not found, please run Connect-HcxServer " } Else { + $cloudConfigUrl = $global:hcxConnection.Server + "/cloudConfigs" + + if($PSVersionTable.PSEdition -eq "Core") { + $cloudvcRequests = Invoke-WebRequest -Uri $cloudConfigUrl -Method GET -Headers $global:hcxConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $cloudvcRequests = Invoke-WebRequest -Uri $cloudConfigUrl -Method GET -Headers $global:hcxConnection.headers -UseBasicParsing + } + + $cloudvcData = ($cloudvcRequests.content | ConvertFrom-Json).data.items + + $tmp = [pscustomobject] @{ + Name = $cloudvcData.cloudName; + Version = $cloudvcData.version; + Build = $cloudvcData.buildNumber; + HCXUUID = $cloudvcData.endpointId; + } + $tmp + } +} + +Function Get-HcxEndpoint { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/24/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + List all HCX endpoints (onPrem and Cloud) + .DESCRIPTION + This cmdlet lists all HCX endpoints (onPrem and Cloud) + .EXAMPLE + Get-HcxEndpoint -cloudVCConnection $cloudVCConnection +#> + Param ( + [Parameter(Mandatory=$true)]$cloudVCConnection + ) + + If (-Not $global:hcxConnection) { Write-error "HCX Auth Token not found, please run Connect-HcxManager " } Else { + #Cloud HCX Manager + $cloudHCXConnectionURL = $global:hcxConnection.Server + "/cloudConfigs" + + if($PSVersionTable.PSEdition -eq "Core") { + $cloudRequests = Invoke-WebRequest -Uri $cloudHCXConnectionURL -Method GET -Headers $global:hcxConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $cloudRequests = Invoke-WebRequest -Uri $cloudHCXConnectionURL -Method GET -Headers $global:hcxConnection.headers -UseBasicParsing + } + $cloudData = ($cloudRequests.Content | ConvertFrom-Json).data.items[0] + + $hcxInventoryUrl = $global:hcxConnection.Server + "/service/inventory/resourcecontainer/list" + + $payload = @{ + "cloud" = @{ + "local"="true"; + "remote"="true"; + } + } + $body = $payload | ConvertTo-Json + + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $hcxInventoryUrl -Body $body -Method POST -Headers $global:hcxConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $hcxInventoryUrl -Body $body -Method POST -Headers $global:hcxConnection.headers -UseBasicParsing + } + if($requests.StatusCode -eq 200) { + $items = ($requests.Content | ConvertFrom-Json).data.items + + $results = @() + foreach ($item in $items) { + $tmp = [pscustomobject] @{ + SourceResourceName = $item.resourceName; + SourceResourceType = $item.resourceType; + SourceResourceId = $item.resourceId; + SourceEndpointName = $item.endpoint.name; + SourceEndpointType = "VC" + SourceEndpointId = $item.endpoint.endpointId; + RemoteResourceName = $cloudVCConnection.name; + RemoteResourceType = "VC" + RemoteResourceId = $cloudVCConnection.InstanceUuid + RemoteEndpointName = $cloudData.cloudName; + RemoteEndpointType = $cloudData.cloudType; + RemoteEndpointId = $cloudData.endpointId; + } + $results+=$tmp + } + return $results + } else { + Write-Error "Failed to list HCX Connection Resources" + } + } +} + +Function New-HcxMigration { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/24/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Initiate a "Bulk" migrations supporting Cold, vMotion, VR or new Cloud Motion + .DESCRIPTION + This cmdlet initiates a "Bulk" migrations supporting Cold, vMotion, VR or new Cloud Motionn + .EXAMPLE + Validate Migration request only + + New-HcxMigration -onPremVCConnection $onPremVC -cloudVCConnection $cloudVC ` + -MigrationType bulkVMotion ` + -VMs @("SJC-CNA-34","SJC-CNA-35","SJC-CNA-36") ` + -NetworkMappings @{"SJC-CORP-WORKLOADS"="sddc-cgw-network-1";"SJC-CORP-INTERNAL-1"="sddc-cgw-network-2";"SJC-CORP-INTERNAL-2"="sddc-cgw-network-3"} ` + -StartTime "Sep 24 2018 1:30 PM" ` + -EndTime "Sep 24 2018 2:30 PM" + .EXAMPLE + Start Migration request + + New-HcxMigration -onPremVCConnection $onPremVC -cloudVCConnection $cloudVC ` + -MigrationType bulkVMotion ` + -VMs @("SJC-CNA-34","SJC-CNA-35","SJC-CNA-36") ` + -NetworkMappings @{"SJC-CORP-WORKLOADS"="sddc-cgw-network-1";"SJC-CORP-INTERNAL-1"="sddc-cgw-network-2";"SJC-CORP-INTERNAL-2"="sddc-cgw-network-3"} ` + -StartTime "Sep 24 2018 1:30 PM" ` + -EndTime "Sep 24 2018 2:30 PM" ` + -MigrationType bulkVMotion +#> + Param ( + [Parameter(Mandatory=$true)][String[]]$VMs, + [Parameter(Mandatory=$true)][Hashtable]$NetworkMappings, + [Parameter(Mandatory=$true)]$onPremVCConnection, + [Parameter(Mandatory=$true)]$cloudVCConnection, + [Parameter(Mandatory=$true)][String]$StartTime, + [Parameter(Mandatory=$true)][String]$EndTime, + [Parameter(Mandatory=$true)][ValidateSet("Cold","vMotion","VR","bulkVMotion")][String]$MigrationType, + [Parameter(Mandatory=$false)]$ValidateOnly=$true + ) + + If (-Not $global:hcxConnection) { Write-error "HCX Auth Token not found, please run Connect-HcxManager " } Else { + $hcxEndpointInfo = Get-HcxEndpoint -cloudVCConnection $cloudVCConnection + + $inputArray = @() + foreach ($vm in $VMs) { + $vmView = Get-View -Server $onPremVCConnection -ViewType VirtualMachine -Filter @{"name"=$vm} + + $cloudResourcePoolName = "Compute-ResourcePool" + $cloudFolderName = "Workloads" + $cloudDatastoreName = "WorkloadDatastore" + $cloudDatacenterName = "SDDC-Datacenter" + + $cloudResourcePool = (Get-ResourcePool -Server $cloudVCConnection -Name $cloudResourcePoolName).ExtensionData + $cloudFolder = (Get-Folder -Server $cloudVCConnection -Name $cloudFolderName).ExtensionData + $cloudDatastore = (Get-Datastore -Server $cloudVCConnection -Name $cloudDatastoreName).ExtensionData + $cloudDatacenter = (Get-Datacenter -Server $cloudVCConnection -Name $cloudDatacenterName).ExtensionData + + $placementArray = @() + $placement = @{ + "containerType"="folder"; + "containerId"=$cloudFolder.MoRef.Value; + "containerName"=$cloudFolderName; + } + $placementArray+=$placement + $placement = @{ + "containerType"="resourcePool"; + "containerId"=$cloudResourcePool.MoRef.Value; + "containerName"=$cloudResourcePoolName; + } + $placementArray+=$placement + $placement = @{ + "containerType"="dataCenter"; + "containerId"=$cloudDatacenter.MoRef.Value; + "containerName"=$cloudDatacenterName; + } + $placementArray+=$placement + + $networkArray = @() + $vmNetworks = $vmView.Network + foreach ($vmNetwork in $vmNetworks) { + if($vmNetwork.Type -eq "Network") { + $sourceNetworkType = "VirtualNetwork" + } else { $sourceNetworkType = $vmNetwork.Type } + + $sourceNetworkRef = New-Object VMware.Vim.ManagedObjectReference + $sourceNetworkRef.Type = $vmNetwork.Type + $sourceNetworkRef.Value = $vmNetwork.Value + $sourceNetwork = Get-View -Server $onPremVCConnection $sourceNetworkRef + + $sourceNetworkName = $sourceNetwork.Name + $destNetworkName = $NetworkMappings[$sourceNetworkName] + + $destNetwork = Get-VDPortGroup -Server $cloudVCConnection -Name $destNetworkName + + if($destNetwork.Id -match "DistributedVirtualPortgroup") { + $destNetworkType = "DistributedVirtualPortgroup" + $destNetworkId = ($destNetwork.Id).Replace("DistributedVirtualPortgroup-","") + } else { + $destNetworkType = "Network" + $destNetworkId = ($destNetwork.Id).Replace("Network-","") + } + + $tmp = @{ + "srcNetworkType" = $sourceNetworkType; + "srcNetworkValue" = $vmNetwork.Value; + "srcNetworkHref" = $vmNetwork.Value; + "srcNetworkName" = $sourceNetworkName; + "destNetworkType" = $destNetworkType; + "destNetworkValue" = $destNetworkId; + "destNetworkHref" = $destNetworkId; + "destNetworkName" = $destNetworkName; + } + $networkArray+=$tmp + } + + $input = @{ + "input" = @{ + "migrationType"=$MigrationType; + "entityDetails" = @{ + "entityId"=$vmView.MoRef.Value; + "entityName"=$vm; + } + "source" = @{ + "endpointType"=$hcxEndpointInfo.SourceEndpointType; + "endpointId"=$hcxEndpointInfo.SourceEndpointId; + "endpointName"=$hcxEndpointInfo.SourceEndpointName; + "resourceType"=$hcxEndpointInfo.SourceResourceType; + "resourceId"=$hcxEndpointInfo.SourceResourceId; + "resourceName"=$hcxEndpointInfo.SourceResourceName; + } + "destination" = @{ + "endpointType"=$hcxEndpointInfo.RemoteEndpointType; + "endpointId"=$hcxEndpointInfo.RemoteEndpointId; + "endpointName"=$hcxEndpointInfo.RemoteEndpointName; + "resourceType"=$hcxEndpointInfo.RemoteResourceType; + "resourceId"=$hcxEndpointInfo.RemoteResourceId; + "resourceName"=$hcxEndpointInfo.RemoteResourceName; + } + "placement" = $placementArray + "storage" = @{ + "datastoreId"=$cloudDatastore.Moref.Value; + "datastoreName"=$cloudDatastoreName; + "diskProvisionType"="thin"; + } + "networks" = @{ + "retainMac" = $true; + "targetNetworks" = $networkArray; + } + "decisionRules" = @{ + "removeSnapshots"=$true; + "removeISOs"=$true; + "forcePowerOffVm"=$false; + "upgradeHardware"=$false; + "upgradeVMTools"=$false; + } + "schedule" = @{} + } + } + $inputArray+=$input + } + + $spec = @{ + "migrations"=$inputArray + } + $body = $spec | ConvertTo-Json -Depth 20 + + Write-Verbose -Message "Pre-Validation JSON Spec: $body" + $hcxMigrationValiateUrl = $global:hcxConnection.Server+ "/migrations?action=validate" + + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $hcxMigrationValiateUrl -Body $body -Method POST -Headers $global:hcxConnection.headers -UseBasicParsing -ContentType "application/json" -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $hcxMigrationValiateUrl -Body $body -Method POST -Headers $global:hcxConnection.headers -UseBasicParsing -ContentType "application/json" + } + + if($requests.StatusCode -eq 200) { + $validationErrors = ($requests.Content|ConvertFrom-Json).migrations.validationInfo.validationResult.errors + if($validationErrors -ne $null) { + Write-Host -Foreground Red "`nThere were validation errors found for this HCX Migration Spec ..." + foreach ($message in $validationErrors) { + Write-Host -Foreground Yellow "`t" $message.message + } + } else { + Write-Host -Foreground Green "`nHCX Pre-Migration Spec successfully validated" + if($ValidateOnly -eq $false) { + try { + $startDateTime = $StartTime | Get-Date + } catch { + Write-Host -Foreground Red "Invalid input for -StartTime, please check for typos" + exit + } + + try { + $endDateTime = $EndTime | Get-Date + } catch { + Write-Host -Foreground Red "Invalid input for -EndTime, please check for typos" + exit + } + + $offset = (Get-TimeZone).GetUtcOffset($startDateTime).TotalMinutes + $offset = [int]($offSet.toString().replace("-","")) + + $schedule = @{ + scheduledFailover = $true; + startYear = $startDateTime.Year; + startMonth = $startDateTime.Month; + startDay = $startDateTime.Day; + startHour = $startDateTime | Get-Date -UFormat %H; + startMinute = $startDateTime | Get-Date -UFormat %M; + endYear = $endDateTime.Year; + endMonth = $endDateTime.Month; + endDay = $endDateTime.Day; + endHour = $endDateTime | Get-Date -UFormat %H; + endMinute = $endDateTime | Get-Date -UFormat %M; + timezoneOffset = $offset; + } + + foreach ($migration in $spec.migrations) { + $migration.input.schedule = $schedule + } + $body = $spec | ConvertTo-Json -Depth 8 + + Write-Verbose -Message "Validated JSON Spec: $body" + $hcxMigrationStartUrl = $global:hcxConnection.Server+ "/migrations?action=start" + + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $hcxMigrationStartUrl -Body $body -Method POST -Headers $global:hcxConnection.headers -UseBasicParsing -ContentType "application/json" -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $hcxMigrationStartUrl -Body $body -Method POST -Headers $global:hcxConnection.headers -UseBasicParsing -ContentType "application/json" + } + + if($requests.StatusCode -eq 200) { + $migrationIds = ($requests.Content | ConvertFrom-Json).migrations.migrationId + Write-Host -ForegroundColor Green "Starting HCX Migration ..." + foreach ($migrationId in $migrationIds) { + Write-Host -ForegroundColor Green "`tMigrationID: $migrationId" + } + } else { + Write-Error "Failed to start HCX Migration" + } + } + } + } else { + Write-Error "Failed to validate HCX Migration spec" + } + } +} + +Function Get-HcxMigration { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/24/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + List all HCX Migrations that are in-progress, have completed or failed + .DESCRIPTION + This cmdlet lists ist all HCX Migrations that are in-progress, have completed or failed + .EXAMPLE + List all HCX Migrations + + Get-HcxMigration + .EXAMPLE + List all running HCX Migrations + + Get-HcxMigration -RunningMigrations + .EXAMPLE + List all HCX Migrations + + Get-HcxMigration -MigrationId +#> + Param ( + [Parameter(Mandatory=$false)][String]$MigrationId, + [Switch]$RunningMigrations + ) + + If (-Not $global:hcxConnection) { Write-error "HCX Auth Token not found, please run Connect-HcxManager " } Else { + $spec = @{} + $body = $spec | ConvertTo-Json + + $hcxQueryUrl = $global:hcxConnection.Server + "/migrations?action=query" + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $hcxQueryUrl -Method POST -body $body -Headers $global:hcxConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $hcxQueryUrl -Method POST -Headers $global:hcxConnection.headers -UseBasicParsing + } + + $migrations = ($requests.content | ConvertFrom-Json).rows + + if($PSBoundParameters.ContainsKey("MigrationId")){ + $migrations = $migrations | where { $_.migrationId -eq $MigrationId } + } + + if($RunningMigrations){ + $migrations = $migrations | where { $_.jobInfo.state -ne "MIGRATE_FAILED" -and $_.jobInfo.state -ne "MIGRATE_CANCELED"-and $_.jobInfo.state -ne "MIGRATED" } + } + + $results = @() + foreach ($migration in $migrations) { + $tmp = [pscustomobject] @{ + ID = $migration.migrationId; + VM = $migration.migrationInfo.entityDetails.entityName; + State = $migration.jobInfo.state; + Progress = ($migration.migrationInfo.progressDetails.progressPercentage).toString() + " %"; + DataCopied = ([math]::round($migration.migrationInfo.progressDetails.diskCopyBytes/1Gb, 2)).toString() + " GB"; + Message = $migration.migrationInfo.message; + InitiatedBy = $migration.jobInfo.username; + CreateDate = $migration.jobInfo.creationDate; + LastUpdated = $migration.jobInfo.lastUpdated; + } + $results+=$tmp + } + $results + } +} + +Function Connect-HcxVAMI { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/16/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Connect to the HCX Enterprise Manager VAMI + .DESCRIPTION + This cmdlet connects to the HCX Enterprise Manager VAMI + .EXAMPLE + Connect-HcxVAMI -Server $HCXServer -Username $VAMIUsername -Password $VAMIPassword +#> + Param ( + [Parameter(Mandatory=$true)][String]$Server, + [Parameter(Mandatory=$true)][String]$Username, + [Parameter(Mandatory=$true)][String]$Password + ) + + $pair = "${Username}:${Password}" + $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair) + $base64 = [System.Convert]::ToBase64String($bytes) + $basicAuthValue = "Basic $base64" + + $headers = @{ + "authorization"="$basicAuthValue" + "Content-Type"="application/json" + "Accept"="application/json" + } + + $global:hcxVAMIConnection = new-object PSObject -Property @{ + 'Server' = "https://${server}:9443"; + 'headers' = $headers + } + $global:hcxVAMIConnection +} + +Function Get-HcxVCConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/16/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Returns the onPrem vCenter Server registered with HCX Manager + .DESCRIPTION + This cmdlet returns the onPrem vCenter Server registered with HCX Manager + .EXAMPLE + Get-HcxVCConfig +#> + If (-Not $global:hcxVAMIConnection) { Write-error "HCX Auth Token not found, please run Connect-HcxVAMI " } Else { + $vcConfigUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/vcenter" + + if($PSVersionTable.PSEdition -eq "Core") { + $vcRequests = Invoke-WebRequest -Uri $vcConfigUrl -Method GET -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $vcRequests = Invoke-WebRequest -Uri $vcConfigUrl -Method GET -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + $vcData = ($vcRequests.content | ConvertFrom-Json).data.items + + $tmp = [pscustomobject] @{ + Name = $vcData.config.name; + Version = $vcData.config.version; + Build = $vcData.config.buildNumber; + UUID = $vcData.config.vcuuid; + HCXUUID = $vcData.config.uuid; + } + $tmp + } +} + +Function Set-HcxLicense { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/16/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Activate HCX Manager with HCX Cloud + .DESCRIPTION + This cmdlet activates HCX Manager with HCX Cloud + .EXAMPLE + Set-HcxLicense -LicenseKey +#> + Param ( + [Parameter(Mandatory=$True)]$LicenseKey + ) + + If (-Not $global:hcxVAMIConnection) { Write-error "HCX VAMI Auth Token not found, please run Connect-HcxVAMI " } Else { + $hcxConfigUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/hcx" + $method = "POST" + + $hcxConfig = @{ + config = @{ + url = "https://connect.hcx.vmware.com"; + activationKey = $LicenseKey; + } + } + + $payload = @{ + data = @{ + items = @($hcxConfig) + } + } + + $body = $payload | ConvertTo-Json -Depth 5 + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$vcConfigUrl`n" + Write-Host -ForegroundColor cyan "[DEBUG]`n$body`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $results = Invoke-WebRequest -Uri $hcxConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $results = Invoke-WebRequest -Uri $hcxConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + } catch { + Write-Host -ForegroundColor Red "`nRequest failed: ($_.Exception)`n" + break + } + + if($results.StatusCode -eq 200) { + Write-Host -ForegroundColor Green "Successfully registered HCX Manager with HCX Cloud" + if($Troubleshoot) { ($results.Content | ConvertFrom-Json).data.items } + } else { + Write-Error "Failed to registered HCX Manager" + } + } +} + +Function Set-HcxVCConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/16/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Registers on-prem vCenter Server with HCX Manager + .DESCRIPTION + This cmdlet registers on-prem vCenter Server with HCX Manager + .EXAMPLE + Set-HcxVC -VIServer -VIUsername -VIPassword +#> + Param ( + [Parameter(Mandatory=$True)]$VIServer, + [Parameter(Mandatory=$True)]$PSCServer, + [Parameter(Mandatory=$True)]$VIUsername, + [Parameter(Mandatory=$True)]$VIPassword, + [Switch]$Troubleshoot + ) + + If (-Not $global:hcxVAMIConnection) { Write-error "HCX VAMI Auth Token not found, please run Connect-HcxVAMI " } Else { + $vcConfigUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/vcenter" + $pscConfigUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/lookupservice" + $method = "POST" + + + $bytes = [System.Text.Encoding]::ASCII.GetBytes($VIPassword) + $base64 = [System.Convert]::ToBase64String($bytes) + + $vcConfig = @{ + config = @{ + url = "https://$VIServer"; + userName = $VIUsername; + password = $base64; + } + } + + $payload = @{ + data = @{ + items = @($vcConfig) + } + } + + $body = $payload | ConvertTo-Json -Depth 5 + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$vcConfigUrl`n" + Write-Host -ForegroundColor cyan "[DEBUG]`n$body`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $results = Invoke-WebRequest -Uri $vcConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $results = Invoke-WebRequest -Uri $vcConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + } catch { + Write-Host -ForegroundColor Red "`nRequest failed: ($_.Exception)`n" + break + } + + if($results.StatusCode -eq 200) { + Write-Host -ForegroundColor Green "Successfully registered vCenter Server with HCX Manager" + if($Troubleshoot) { ($results.Content | ConvertFrom-Json).data.items.config } + + $pscConfig = @{ + config = @{ + lookupServiceUrl = "https://$PSCServer" + providerType = "PSC" + } + } + + $payload = @{ + data = @{ + items = @($pscConfig) + } + } + + $body = $payload | ConvertTo-Json -Depth 5 + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$pscConfigUrl`n" + Write-Host -ForegroundColor cyan "[DEBUG]`n$body`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $results = Invoke-WebRequest -Uri $pscConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $results = Invoke-WebRequest -Uri $pscConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + } catch { + Write-Host -ForegroundColor Red "`nRequest failed: ($_.Exception)`n" + break + } + + if($results.StatusCode -eq 200) { + Write-Host -ForegroundColor Green "Successfully registered PSC with HCX Manager" + if($Troubleshoot) { ($results.Content | ConvertFrom-Json).data.items.config } + + } else { + Write-Error "Failed to registered PSC Server" + } + } else { + Write-Error "Failed to registered vCenter Server" + } + } +} + +Function Get-HcxNSXConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/16/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Returns the onPrem NSX-V Server registered with HCX Manager + .DESCRIPTION + This cmdlet returns the onPrem NSX-V Server registered with HCX Manager + .EXAMPLE + Get-HcxNSXConfig +#> + If (-Not $global:hcxVAMIConnection) { Write-error "HCX Auth Token not found, please run Connect-HcxVAMI " } Else { + $nsxConfigUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/nsx" + + if($PSVersionTable.PSEdition -eq "Core") { + $nsxRequests = Invoke-WebRequest -Uri $nsxConfigUrl -Method GET -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $nsxRequests = Invoke-WebRequest -Uri $nsxConfigUrl -Method GET -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + $nsxData = ($nsxRequests.content | ConvertFrom-Json).data.items + + $tmp = [pscustomobject] @{ + Name = $nsxData.config.url; + Version = $nsxData.config.version; + HCXUUID = $nsxData.config.uuid; + } + $tmp + } +} + +Function Set-HcxNSXConfig { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/16/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Registers on-prem NSX-V Server with HCX Manager + .DESCRIPTION + This cmdlet registers on-prem NSX-V Server with HCX Manager + .EXAMPLE + Set-HcxNSXConfig -NSXServer -NSXUsername -NSXPassword +#> + Param ( + [Parameter(Mandatory=$True)]$NSXServer, + [Parameter(Mandatory=$True)]$NSXUsername, + [Parameter(Mandatory=$True)]$NSXPassword, + [Switch]$Troubleshoot + ) + + If (-Not $global:hcxVAMIConnection) { Write-error "HCX VAMI Auth Token not found, please run Connect-HcxVAMI " } Else { + $nsxConfigUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/nsx" + $method = "POST" + + $bytes = [System.Text.Encoding]::ASCII.GetBytes($NSXPassword) + $base64 = [System.Convert]::ToBase64String($bytes) + + $nsxConfig = @{ + config = @{ + url = "https://$NSXServer"; + userName = $NSXUsername; + password = $base64; + } + } + + $payload = @{ + data = @{ + items = @($nsxConfig) + } + } + + $body = $payload | ConvertTo-Json -Depth 5 + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$nsxConfigUrl`n" + Write-Host -ForegroundColor cyan "[DEBUG]`n$body`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $results = Invoke-WebRequest -Uri $nsxConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $results = Invoke-WebRequest -Uri $nsxConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + } catch { + Write-Host -ForegroundColor Red "`nRequest failed: ($_.Exception)`n" + break + } + + if($results.StatusCode -eq 200) { + Write-Host -ForegroundColor Green "Successfully registered NSX Server with HCX Manager" + if($Troubleshoot) { ($results.Content | ConvertFrom-Json).data.items.config } + } else { + Write-Error "Failed to registered NSX Server" + } + return $config + } +} + +Function Get-HcxCity { + <# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/16/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Returns the available HCX Location based on user City and Country input + .DESCRIPTION + This cmdlet returns the available HCX Location based on user City and Country input + .EXAMPLE + Get-HcxCity -City -Country + #> + Param ( + [Parameter(Mandatory=$True)]$City, + [Switch]$Troubleshoot + ) + + If (-Not $global:hcxVAMIConnection) { Write-error "HCX VAMI Auth Token not found, please run Connect-HcxVAMI " } Else { + $citySearchUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/searchCities?searchString=$City" + $method = "GET" + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$citySearchUrl`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $results = Invoke-WebRequest -Uri $citySearchUrl -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $results = Invoke-WebRequest -Uri $citySearchUrl -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + } catch { + Write-Host -ForegroundColor Red "`nRequest failed: ($_.Exception)`n" + break + } + + if($results.StatusCode -eq 200) { + Write-Host -ForegroundColor Green "Successfully returned results for City search: $City" + + $cityDetails = ($results.Content | ConvertFrom-Json).items + $cityDetails | select City,Country + } else { + Write-Error "Failed to search for city $City" + } + } + } + +Function Get-HcxLocation { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/16/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Returns the registered City/Country location for HCX Manager + .DESCRIPTION + This cmdlet returns the registered City/Country location for HCX Manager + .EXAMPLE + Get-HcxLocation +#> + If (-Not $global:hcxVAMIConnection) { Write-error "HCX Auth Token not found, please run Connect-HcxVAMI " } Else { + $locationConfigUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/location" + + if($PSVersionTable.PSEdition -eq "Core") { + $locationRequests = Invoke-WebRequest -Uri $locationConfigUrl -Method GET -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $locationRequests = Invoke-WebRequest -Uri $locationConfigUrl -Method GET -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + ($locationRequests.content | ConvertFrom-Json) + } +} + +Function Set-HcxLocation { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/16/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Register HCX Manager to a specific City/Country + .DESCRIPTION + This cmdlet register HCX Manager to a specific City/Country + .EXAMPLE + Set-HcxLocation -City -Country +#> + Param ( + [Parameter(Mandatory=$True)]$City, + [Parameter(Mandatory=$True)]$Country, + [Switch]$Troubleshoot + ) + + If (-Not $global:hcxVAMIConnection) { Write-error "HCX VAMI Auth Token not found, please run Connect-HcxVAMI " } Else { + $citySearchUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/searchCities?searchString=$City" + $method = "GET" + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$citySearchUrl`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $results = Invoke-WebRequest -Uri $citySearchUrl -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $results = Invoke-WebRequest -Uri $citySearchUrl -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + } catch { + Write-Host -ForegroundColor Red "`nRequest failed: ($_.Exception)`n" + break + } + + if($results.StatusCode -eq 200) { + if($Troubleshoot) { ($results.Content | ConvertFrom-Json).items } + + $locationConfigUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/location" + $method = "PUT" + + $cityDetails = ($results.Content | ConvertFrom-Json).items + $cityDetails = $cityDetails | where { $_.city -eq $City -and $_.country -match $Country } + + if(-not $cityDetails) { + Write-Host -ForegroundColor Red "Invalid input for City and/or Country, please provide the exact input from Get-HcxCity cmdlet" + break + } + + $locationConfig = @{ + city = $cityDetails.city; + country = $cityDetails.country; + province = $cityDetails.province; + latitude = $cityDetails.latitude; + longitude = $cityDetails.longitude; + } + + $body = $locationConfig | ConvertTo-Json -Depth 5 + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$locationConfigUrl`n" + Write-Host -ForegroundColor cyan "[DEBUG]`n$body`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $results = Invoke-WebRequest -Uri $locationConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $results = Invoke-WebRequest -Uri $locationConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + } catch { + Write-Host -ForegroundColor Red "`nRequest failed: ($_.Exception)`n" + break + } + + if($results.StatusCode -eq 204) { + Write-Host -ForegroundColor Green "Successfully registered datacenter location $City to HCX Manager" + } else { + Write-Error "Failed to registerd datacenter location in HCX Manager" + } + } else { + Write-Error "Failed to search for city $City" + } + } +} +Function Get-HcxRoleMapping { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/16/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Returns the System Admin and Enterprise User Group role mappings for HCX Manager + .DESCRIPTION + This cmdlet returns the System Admin and Enterprise User Group role mappings for HCX Manager + .EXAMPLE + Get-HcxRoleMapping +#> + If (-Not $global:hcxVAMIConnection) { Write-error "HCX Auth Token not found, please run Connect-HcxVAMI " } Else { + $roleConfigUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/roleMappings" + + if($PSVersionTable.PSEdition -eq "Core") { + $roleRequests = Invoke-WebRequest -Uri $roleConfigUrl -Method GET -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $roleRequests = Invoke-WebRequest -Uri $roleConfigUrl -Method GET -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + ($roleRequests.content | ConvertFrom-Json) + } +} + +Function Set-HcxRoleMapping { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/16/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Configures the System Admin and Enterprise User Group role mappings for HCX Manager + .DESCRIPTION + This cmdlet configures the System Admin and Enterprise User Group role mappings for HCX Manager + .EXAMPLE + Set-HcxRoleMapping -SystemAdminGroup @("DOMAIN\GROUP") -EnterpriseAdminGroup @("DOMAIN\GROUP") +#> + Param ( + [Parameter(Mandatory=$True)][String[]]$SystemAdminGroup, + [Parameter(Mandatory=$True)][String[]]$EnterpriseAdminGroup, + [Switch]$Troubleshoot + ) + + If (-Not $global:hcxVAMIConnection) { Write-error "HCX VAMI Auth Token not found, please run Connect-HcxVAMI " } Else { + $roleConfigUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/roleMappings" + $method = "PUT" + + $roleConfig = @() + $systemAdminRole = @{ + role = "System Administrator"; + userGroups = $SystemAdminGroup + } + $enterpriseAdminRole = @{ + role = "Enterprise Administrator" + userGroups = $EnterpriseAdminGroup + } + $roleConfig+=$systemAdminRole + $roleConfig+=$enterpriseAdminRole + + $body = $roleConfig | ConvertTo-Json -Depth 5 + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$locationConfigUrl`n" + Write-Host -ForegroundColor cyan "[DEBUG]`n$body`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $results = Invoke-WebRequest -Uri $roleConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $results = Invoke-WebRequest -Uri $roleConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + } catch { + Write-Host -ForegroundColor Red "`nRequest failed: ($_.Exception)`n" + break + } + + if($results.StatusCode -eq 200) { + Write-Host -ForegroundColor Green "Successfully updated vSphere Group Mappings in HCX Manager" + } else { + Write-Error "Failed to update vSphere Group Mappings" + } + } +} + +Function Get-HcxProxy { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 10/31/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Returns the proxy settings for HCX Manager + .DESCRIPTION + This cmdlet returns the proxy settings for HCX Manager + .EXAMPLE + Get-HcxProxy +#> + If (-Not $global:hcxVAMIConnection) { Write-error "HCX Auth Token not found, please run Connect-HcxVAMI " } Else { + $proxyConfigUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/proxy" + + if($PSVersionTable.PSEdition -eq "Core") { + $proxyRequests = Invoke-WebRequest -Uri $proxyConfigUrl -Method GET -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $proxyRequests = Invoke-WebRequest -Uri $proxyConfigUrl -Method GET -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + $proxySettings = ($proxyRequests.content | ConvertFrom-Json).data.items + if($proxyRequests) { + $proxySettings.config + } + } +} + +Function Set-HcxProxy { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 10/31/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Configure proxy settings on HCX Manager + .DESCRIPTION + This cmdlet configure proxy settings on HCX Manager + .EXAMPLE + Set-HcxProxy -ProxyServer proxy.vmware.com -ProxyPort 3124 + .EXAMPLE + Set-HcxProxy -ProxyServer proxy.vmware.com -ProxyPort 3124 -ProxyUser foo -ProxyPassword bar +#> + Param ( + [Parameter(Mandatory=$True)]$ProxyServer, + [Parameter(Mandatory=$True)]$ProxyPort, + [Parameter(Mandatory=$False)]$ProxyUser, + [Parameter(Mandatory=$False)]$ProxyPassword, + [Switch]$Troubleshoot + ) + + If (-Not $global:hcxVAMIConnection) { Write-error "HCX VAMI Auth Token not found, please run Connect-HcxVAMI " } Else { + $proxyConfigUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/proxy" + $method = "POST" + + if(-not $ProxyUser) { $ProxyUser = ""} + if(-not $ProxyPassword) { $ProxyPassword = ""} + + $proxyConfig = @{ + config = @{ + proxyHost = "$ProxyServer"; + proxyPort = "$ProxyPort"; + nonProxyHosts = ""; + userName = "$ProxyUser"; + password = "$ProxyPassword"; + } + } + + $payload = @{ + data = @{ + items = @($proxyConfig) + } + } + + $body = $payload | ConvertTo-Json -Depth 5 + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$proxyConfigUrl`n" + Write-Host -ForegroundColor cyan "[DEBUG]`n$body`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $results = Invoke-WebRequest -Uri $proxyConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $results = Invoke-WebRequest -Uri $proxyConfigUrl -Body $body -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + } catch { + Write-Host -ForegroundColor Red "`nRequest failed: ($_.Exception)`n" + break + } + + if($results.StatusCode -eq 200) { + Write-Host -ForegroundColor Green "Successfully updated proxy settings in HCX Manager" + if($Troubleshoot) { ($results.Content | ConvertFrom-Json).data.items.config } + } else { + Write-Error "Failed to update proxy settings" + } + } +} + +Function Remove-HcxProxy { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 10/31/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Returns the proxy settings for HCX Manager + .DESCRIPTION + This cmdlet returns the proxy settings for HCX Manager + .EXAMPLE + Remove-HcxProxy +#> + If (-Not $global:hcxVAMIConnection) { Write-error "HCX Auth Token not found, please run Connect-HcxVAMI " } Else { + $roleConfigUrl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/proxy" + + if($PSVersionTable.PSEdition -eq "Core") { + $proxyRequests = Invoke-WebRequest -Uri $roleConfigUrl -Method GET -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $proxyRequests = Invoke-WebRequest -Uri $roleConfigUrl -Method GET -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + $proxySettings = ($proxyRequests.content | ConvertFrom-Json).data.items + if($proxyRequests) { + $proxyUUID = $proxySettings.config.UUID + + $deleteProxyConfigURl = $global:hcxVAMIConnection.Server + "/api/admin/global/config/proxy/$proxyUUID" + $method = "DELETE" + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $results = Invoke-WebRequest -Uri $deleteProxyConfigURl -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing -SkipCertificateCheck + } else { + $results = Invoke-WebRequest -Uri $deleteProxyConfigURl -Method $method -Headers $global:hcxVAMIConnection.headers -UseBasicParsing + } + } catch { + Write-Host -ForegroundColor Red "`nRequest failed: ($_.Exception)`n" + break + } + + if($results.StatusCode -eq 200) { + Write-Host -ForegroundColor Green "Successfully deleted proxy settings in HCX Manager" + } else { + Write-Error "Failed to delete proxy settings" + } + } else { + Write-Warning "No proxy settings were configured" + } + } +} \ No newline at end of file diff --git a/Modules/VMware.Hosted/VMware.Hosted.psd1 b/Modules/VMware.Hosted/VMware.Hosted.psd1 new file mode 100644 index 0000000..59d5b53 --- /dev/null +++ b/Modules/VMware.Hosted/VMware.Hosted.psd1 @@ -0,0 +1,18 @@ +@{ + ModuleToProcess = 'VMware.Hosted.psm1' + ModuleVersion = '1.0.0.0' + GUID = '11393D09-D6B8-4E79-B9BC-247F1BE66683' + Author = 'William Lam' + CompanyName = 'primp-industries.com' + Copyright = '(c) 2017. All rights reserved.' + Description = 'Powershell Module for VMware Fusion 10 REST API' + PowerShellVersion = '5.0' + FunctionsToExport = 'Get-HostedCommand','Connect-HostedServer','Disconnect-HostedServer','Get-HostedVM','Start-HostedVM','Stop-HostedVM','Suspend-HostedVM','Resume-HostedVM','New-HostedVM','Remove-HostedVM','Get-HostedVMSharedFolder','New-HostedVMSharedFolder','Remove-HostedVMSharedFolder','Get-HostedVMNic','Get-HostedNetworks' + PrivateData = @{ + PSData = @{ + Tags = @('Fusion','REST','vmrest') + LicenseUri = 'https://www.tldrlegal.com/l/mit' + ProjectUri = 'https://github.com/lamw/PowerCLI-Example-Scripts/tree/master/Modules/VMware.Hosted' + } + } +} \ No newline at end of file diff --git a/Modules/VMware.Hosted/VMware.Hosted.psm1 b/Modules/VMware.Hosted/VMware.Hosted.psm1 new file mode 100644 index 0000000..4997dee --- /dev/null +++ b/Modules/VMware.Hosted/VMware.Hosted.psm1 @@ -0,0 +1,501 @@ +Function Get-Confirmation { + $choice = "" + while ($choice -notmatch "[y|n]"){ + $choice = read-host "Do you want to continue? (Y/N)" + } + if($choice -ne 'y') { + break + } +} + +Function Connect-HostedServer { + Param ( + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Server, + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Username, + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Password, + [parameter(Mandatory=$false,ValueFromPipeline=$true)][string]$Protocol = "http", + [parameter(Mandatory=$false,ValueFromPipeline=$true)][int]$Port = 8697 + ) + $pair = $Username+":"+$Password + $bytes = [System.Text.Encoding]::ASCII.GetBytes($pair) + $base64 = [System.Convert]::ToBase64String($bytes) + $basicAuthValue = "Basic $base64" + $headers = @{Authorization = $basicAuthValue} + + $Global:DefaultHostedServer = [pscustomobject] @{ + Server=$Protocol + "://" + $server + ":$Port/api"; + Protcol=$Protocol + Headers=$headers + } + + if($DefaultHostedServer.Protcol -eq "https") { + # PowerShell Core has a nice -SkipCertificateCheck but looks like Windows does NOT :( + if($PSVersionTable.PSEdition -eq "Core") { + $Global:fusionCommand = "Invoke-Webrequest -SkipCertificateCheck " + } else { + # Needed for Windows PowerShell to handle HTTPS scenario + # https://stackoverflow.com/a/15627483 + $Provider = New-Object Microsoft.CSharp.CSharpCodeProvider + $Compiler = $Provider.CreateCompiler() + $Params = New-Object System.CodeDom.Compiler.CompilerParameters + $Params.GenerateExecutable = $false + $Params.GenerateInMemory = $true + $Params.IncludeDebugInformation = $false + $Params.ReferencedAssemblies.Add("System.DLL") > $null + $TASource=@' + namespace Local.ToolkitExtensions.Net.CertificatePolicy + { + public class TrustAll : System.Net.ICertificatePolicy + { + public bool CheckValidationResult(System.Net.ServicePoint sp,System.Security.Cryptography.X509Certificates.X509Certificate cert, System.Net.WebRequest req, int problem) + { + return true; + } + } + } +'@ + $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource) + $TAAssembly=$TAResults.CompiledAssembly + ## We create an instance of TrustAll and attach it to the ServicePointManager + $TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll") + [System.Net.ServicePointManager]::CertificatePolicy = $TrustAll + $Global:fusionCommand = "Invoke-Webrequest " + } + } else { + $Global:fusionCommand = "Invoke-Webrequest " + } + $Global:DefaultHostedServer +} + +Function Disconnect-HostedServer { + Param ( + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Server + ) + + $Global:DefaultHostedServer = $null +} + +Function Get-HostedVM { + Param ( + [parameter(Mandatory=$false,ValueFromPipeline=$true)][string]$Id + ) + + if(!$Global:DefaultHostedServer) { + Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer" + exit + } + + if($Id) { + $vmUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + try { + $params = "-Headers `$Global:DefaultHostedServer.Headers -Uri $vmUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`"" + $command = $Global:fusionCommand + $params + $vm = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Invalid VM Id $Id" + break + } + + $vmIPUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/ip" + try { + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmIPUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`"" + $command = $Global:fusionCommand + $params + $vmIPResults = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + $vmIP = $vmIPResults.ip + } catch { + $vmIP = "N/A" + } + + $vmPowerUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/power" + try { + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`"" + $command = $Global:fusionCommand + $params + $vmPower = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + $vmPower = "N/A" + } + + $results = [pscustomobject] @{ + Id = $vm.Id; + CPU = $vm.Cpu.processors; + Memory = $vm.Memory; + PowerState = $vmPower.power_state; + IPAddress = $vmIP; + } + $results + } else { + $uri = $Global:DefaultHostedServer.Server + "/vms" + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $uri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`"" + $command = $Global:fusionCommand + $params + try { + Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Failed to list VMs" + } + } +} + +Function Start-HostedVM { + Param ( + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id + ) + + if(!$Global:DefaultHostedServer) { + Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer" + exit + } + + $vmPowerUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/power" + try { + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`"" + $command = $Global:fusionCommand + $params + $vmPower = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Invalid VM Id $Id" + break + } + + if($vmPower.power_state -eq "poweredOff" -or $vmPower.power_state -eq "suspended") { + try { + Write-Host "Powering on VM $Id ..." + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method PUT -ContentType `"application/vnd.vmware.vmw.rest-v1+json`" -Body `"on`"" + $command = $Global:fusionCommand + $params + $vm = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Unable to Power On VM $Id" + break + } + } else { + Write-Host "VM $Id is already Powered On" + } +} + +Function Stop-HostedVM { + Param ( + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id, + [parameter(Mandatory=$false,ValueFromPipeline=$true)][boolean]$Soft, + [parameter(Mandatory=$false,ValueFromPipeline=$true)][boolean]$Confirm = $true + ) + + if(!$Global:DefaultHostedServer) { + Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer" + exit + } + + if($Confirm) { + Get-Confirmation + } + + $vmPowerUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/power" + try { + $params += "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`"" + $command = $Global:fusionCommand + $params + $vmPower = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Invalid VM Id $Id" + break + } + + if($vmPower.power_state -eq "poweredOn") { + if($Soft) { + try { + Write-Host "Shutting down VM $Id ..." + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method PUT -ContentType `"application/vnd.vmware.vmw.rest-v1+json`" -Body `"shutdown`"" + $command = $Global:fusionCommand + $params + $vm = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Unable to Shutdown VM $Id" + break + } + } else { + try { + Write-Host "Powering off VM $Id ..." + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method PUT -ContentType `"application/vnd.vmware.vmw.rest-v1+json`" -Body `"off`"" + $command = $Global:fusionCommand + $params + $vm = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Unable to Power Off VM $Id" + break + } + } + } else { + Write-Host "VM $Id is already Powered Off" + } +} + +Function Suspend-HostedVM { + Param ( + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id, + [parameter(Mandatory=$false,ValueFromPipeline=$true)][boolean]$Confirm + ) + + if(!$Global:DefaultHostedServer) { + Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer" + exit + } + + if($Confirm) { + Get-Confirmation + } + + $vmPowerUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/power" + try { + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`"" + $command = $Global:fusionCommand + $params + $vmPower = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Invalid VM Id $Id" + break + } + + if($vmPower.power_state -eq "poweredOn") { + try { + Write-Host "Suspending VM $Id ..." + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method PUT -ContentType `"application/vnd.vmware.vmw.rest-v1+json`" -Body `"suspend`"" + $command = $Global:fusionCommand + $params + $vm = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Unable to suspend VM $Id" + break + } + } else { + Write-Host "VM $Id can not be suspended because it is either Powered Off or Suspended" + } +} + +Function Resume-HostedVM { + Param ( + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id + ) + + if(!$Global:DefaultHostedServer) { + Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer" + exit + } + + $vmPowerUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/power" + try { + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmPowerUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`"" + $command = $Global:fusionCommand + $params + $vmPower = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Invalid VM Id $Id" + break + } + + if($vmPower.power_state -eq "suspended") { + try { + Start-HostedVM -Id $Id + } catch { + Write-host -ForegroundColor Red "Unable to Resume VM $Id" + break + } + } else { + Write-Host "VM $Id is not Suspended" + } +} + +Function New-HostedVM { + Param ( + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$ParentId, + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Name + ) + + if(!$Global:DefaultHostedServer) { + Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer" + exit + } + + $vm = Get-HostedVM -Id $ParentId + + if($vm -match "Invalid VM Id") { + Write-host -ForegroundColor Red "Unable to find existing VM Id $ParentId" + break + } + + $vmUri = $Global:DefaultHostedServer.Server + "/vms" + $body = @{"ParentId"="$ParentId";"Name"=$Name} + $body = $body | ConvertTo-Json + + try { + Write-Host "Cloning VM $ParentId to $Name ..." + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmUri -Method POST -ContentType `"application/vnd.vmware.vmw.rest-v1+json`" -Body `$body" + $command = $Global:fusionCommand + $params + Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Failed to Clone VM Id $ParentId" + break + } +} + +Function Remove-HostedVM { + Param ( + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id, + [parameter(Mandatory=$false,ValueFromPipeline=$true)][boolean]$Confirm = $true + ) + + if(!$Global:DefaultHostedServer) { + Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer" + exit + } + + $vm = Get-HostedVM -Id $Id + + if($vm -match "Invalid VM Id") { + Write-host -ForegroundColor Red "Unable to find existing VM Id $Id" + break + } + + if($Confirm) { + Get-Confirmation + } + + $vmUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + try { + Write-Host "Deleting VM Id $Id ..." + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmUri -Method DELETE -ContentType `"application/vnd.vmware.vmw.rest-v1+json`"" + $command = $Global:fusionCommand + $params + Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Failed to Delete VM Id $Id" + break + } +} + +Function Get-HostedVMSharedFolder { + Param ( + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id + ) + + if(!$Global:DefaultHostedServer) { + Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer" + exit + } + + $folderUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/sharedfolders" + try { + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $folderUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`"" + $command = $Global:fusionCommand + $params + Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Invalid VM Id $Id" + break + } +} + +Function New-HostedVMSharedFolder { + Param ( + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id, + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$FolderName, + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$HostPath + ) + + if(!$Global:DefaultHostedServer) { + Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer" + exit + } + + $body = @{"folder_id"="$FolderName";"host_path"=$HostPath;"flags"=4} + $body = $body | ConvertTo-Json + + $folderUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/sharedfolders" + try { + Write-Host "Creating new Shared Folder $FolderName to $HostPath for VM Id $Id ..." + $params += "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $folderUri -Method POST -ContentType `"application/vnd.vmware.vmw.rest-v1+json`" -Body `$body" + $command = $Global:fusionCommand + $params + Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Failed to create Shared Folder for VM Id $Id" + break + } +} + +Function Remove-HostedVMSharedFolder { + Param ( + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id, + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$FolderName, + [parameter(Mandatory=$false,ValueFromPipeline=$true)][boolean]$Confirm = $true + ) + + if(!$Global:DefaultHostedServer) { + Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer" + exit + } + + if($Confirm) { + Get-Confirmation + } + + $folderUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/sharedfolders/" + $FolderName + try { + Write-Host "Removing Shared Folder $FolderName for VM Id $Id ..." + $params += "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $folderUri -Method DELETE -ContentType `"application/vnd.vmware.vmw.rest-v1+json`"" + $command = $Global:fusionCommand + $params + Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Failed to remove Shared Folder for VM Id $Id" + break + } +} + +Function Get-HostedVMNic { + Param ( + [parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$Id + ) + + if(!$Global:DefaultHostedServer) { + Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer" + exit + } + + $vmNicUri = $Global:DefaultHostedServer.Server + "/vms/" + $Id + "/nic" + try { + $params += "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $vmNicUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`"" + $command = $Global:fusionCommand + $params + $vmNics = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Invalid VM Id $Id" + break + } + + $results = @() + foreach ($vmNic in $vmNics.nics) { + $tmp = [pscustomobject] @{ + Index = $vmNic.index; + Type = $vmNic.Type; + VMnet = $vmNic.Vmnet; + } + $results+=$tmp + } + $results +} + +Function Get-HostedNetworks { + if(!$Global:DefaultHostedServer) { + Write-Host -ForegroundColor Red "You are not connected to Hosted Server, please run Connect-HostedServer" + exit + } + + $networksUri = $Global:DefaultHostedServer.Server + "/vmnet" + try { + $params = "-UseBasicParsing -Headers `$Global:DefaultHostedServer.Headers -Uri $networksUri -Method GET -ContentType `"application/vnd.vmware.vmw.rest-v1+json`"" + $command = $Global:fusionCommand + $params + $networks = Invoke-Expression -Command $command | ConvertFrom-Json -ErrorAction Stop + } catch { + Write-host -ForegroundColor Red "Unable to retrieve Networks" + break + } + + $results = @() + foreach ($network in $networks.vmnets) { + $tmp = [pscustomobject] @{ + Name = $network.Name; + Type = $network.Type; + DHCP = $network.Dhcp; + Network = $network.subnet; + Netmask = $network.mask; + } + $results+=$tmp + } + $results +} \ No newline at end of file diff --git a/Modules/VMware.Hv.Helper/Json/Farm/AutomatedInstantCloneFarm.json b/Modules/VMware.Hv.Helper/Json/Farm/AutomatedInstantCloneFarm.json new file mode 100644 index 0000000..605e6a5 --- /dev/null +++ b/Modules/VMware.Hv.Helper/Json/Farm/AutomatedInstantCloneFarm.json @@ -0,0 +1,98 @@ +{ + "Type": "AUTOMATED", + "Data": { + "Name": "ICFarmJson", + "DisplayName": "FarmJsonTest", + "AccessGroup": "Root", + "Description": "created IC Farm from PS via JSON", + "Enabled": null, + "Deleting": false, + "Settings": { + "DisconnectedSessionTimeoutPolicy" : "NEVER", + "DisconnectedSessionTimeoutMinutes" : 1, + "EmptySessionTimeoutPolicy" : "AFTER", + "EmptySessionTimeoutMinutes" : 1, + "LogoffAfterTimeout" : false + }, + "Desktop": null, + "DisplayProtocolSettings": { + "DefaultDisplayProtocol" : "PCOIP", + "AllowDisplayProtocolOverride" : false, + "EnableHTMLAccess" : false + }, + "ServerErrorThreshold": null, + "MirageConfigurationOverrides": { + "OverrideGlobalSetting" : false, + "Enabled" : false, + "Url" : null + } + }, + "AutomatedFarmSpec": { + "ProvisioningType": "INSTANT_CLONE_ENGINE", + "VirtualCenter": null, + "RdsServerNamingSpec": { + "NamingMethod": "PATTERN", + "PatternNamingSettings": { + "NamingPattern": "ICFarmVMPS", + "MaxNumberOfRDSServers": 1 + } + }, + "VirtualCenterProvisioningSettings": { + "EnableProvisioning": true, + "StopProvisioningOnError": true, + "MinReadyVMsOnVComposerMaintenance": 0, + "VirtualCenterProvisioningData": { + "ParentVm": "vm-rdsh-ic", + "Snapshot": "snap_5", + "Datacenter": null, + "VmFolder": "Instant_Clone_VMs", + "HostOrCluster": "vimal-cluster", + "ResourcePool": "vimal-cluster" + }, + "VirtualCenterStorageSettings": { + "Datastores": [ + { + "Datastore": "Datastore1", + "StorageOvercommit": "UNBOUNDED" + } + ], + "UseVSan": false, + "ViewComposerStorageSettings": { + "UseSeparateDatastoresReplicaAndOSDisks": false, + "ReplicaDiskDatastore": null, + "UseNativeSnapshots": false, + "SpaceReclamationSettings": { + "ReclaimVmDiskSpace": false, + "ReclamationThresholdGB": null, + "BlackoutTimes": null + } + } + }, + "VirtualCenterNetworkingSettings": { + "Nics": null + } + }, + "VirtualCenterManagedCommonSettings": { + "TransparentPageSharingScope": "VM" + }, + "CustomizationSettings": { + "CustomizationType": "CLONE_PREP", + "DomainAdministrator": null, + "AdContainer": "CN=Computers", + "ReusePreExistingAccounts": false, + "ClonePrepCustomizationSettings": { + "InstantCloneEngineDomainAdministrator": null, + "PowerOffScriptName": null, + "PowerOffScriptParameters": null, + "PostSynchronizationScriptName": null, + "PostSynchronizationScriptParameters": null + } + }, + "RdsServerMaxSessionsData": { + "MaxSessionsType": "UNLIMITED", + "MaxSessions": null + } + }, + "ManualFarmSpec": null, + "NetBiosName" : "ad-vimalg" +} diff --git a/Modules/VMware.Hv.Helper/New-HVFarm/AutomatedLinkedCloneFarm.json b/Modules/VMware.Hv.Helper/Json/Farm/AutomatedLinkedCloneFarm.json similarity index 84% rename from Modules/VMware.Hv.Helper/New-HVFarm/AutomatedLinkedCloneFarm.json rename to Modules/VMware.Hv.Helper/Json/Farm/AutomatedLinkedCloneFarm.json index 3706374..32c3f9a 100644 --- a/Modules/VMware.Hv.Helper/New-HVFarm/AutomatedLinkedCloneFarm.json +++ b/Modules/VMware.Hv.Helper/Json/Farm/AutomatedLinkedCloneFarm.json @@ -1,17 +1,31 @@ { "Type": "AUTOMATED", "Data": { - "Name": "LCFarmTest", - "DisplayName": "Ankit LC Farm Test", + "Name": "LCFarmJson", + "DisplayName": "FarmJsonTest", "AccessGroup": "Root", - "Description": "created LC Farm from PS", + "Description": "created LC Farm from PS via JSON", "Enabled": null, "Deleting": false, - "Settings": null, + "Settings": { + "DisconnectedSessionTimeoutPolicy" : "NEVER", + "DisconnectedSessionTimeoutMinutes" : 1, + "EmptySessionTimeoutPolicy" : "AFTER", + "EmptySessionTimeoutMinutes" : 1, + "LogoffAfterTimeout" : false + }, "Desktop": null, - "DisplayProtocolSettings": null, + "DisplayProtocolSettings": { + "DefaultDisplayProtocol" : "PCOIP", + "AllowDisplayProtocolOverride" : false, + "EnableHTMLAccess" : false + }, "ServerErrorThreshold": null, - "MirageConfigurationOverrides": null + "MirageConfigurationOverrides": { + "OverrideGlobalSetting" : false, + "Enabled" : false, + "Url" : null + } }, "AutomatedFarmSpec": { "ProvisioningType": "VIEW_COMPOSER", @@ -19,7 +33,7 @@ "RdsServerNamingSpec": { "NamingMethod": "PATTERN", "PatternNamingSettings": { - "NamingPattern": "LCFarmVM_PS", + "NamingPattern": "LCFarmVMPS", "MaxNumberOfRDSServers": 1 } }, @@ -28,17 +42,17 @@ "StopProvisioningOnError": true, "MinReadyVMsOnVComposerMaintenance": 0, "VirtualCenterProvisioningData": { - "ParentVm": "Win_Server_2012_R2", - "Snapshot": "Snap_RDS", + "ParentVm": "RDSServer", + "Snapshot": "RDS_SNAP1", "Datacenter": null, - "VmFolder": "AnkitPoolVM", - "HostOrCluster": "cls", - "ResourcePool": "cls" + "VmFolder": "Praveen", + "HostOrCluster": "CS-1", + "ResourcePool": "CS-1" }, "VirtualCenterStorageSettings": { "Datastores": [ { - "Datastore": "datastore1 (5)", + "Datastore": "Datastore1", "StorageOvercommit": "UNBOUNDED" } ], @@ -67,7 +81,7 @@ "AdContainer": "CN=Computers", "ReusePreExistingAccounts": false, "SysprepCustomizationSettings": { - "CustomizationSpec": "RDSH_Cust2" + "CustomizationSpec": "PraveenCust" } }, "RdsServerMaxSessionsData": { @@ -76,5 +90,5 @@ } }, "ManualFarmSpec": null, - "NetBiosName" : "adankit" + "NetBiosName" : "adviewdev" } diff --git a/Modules/VMware.Hv.Helper/New-HVFarm/ManualFarm.json b/Modules/VMware.Hv.Helper/Json/Farm/ManualFarm.json similarity index 51% rename from Modules/VMware.Hv.Helper/New-HVFarm/ManualFarm.json rename to Modules/VMware.Hv.Helper/Json/Farm/ManualFarm.json index cf674c1..3dd1678 100644 --- a/Modules/VMware.Hv.Helper/New-HVFarm/ManualFarm.json +++ b/Modules/VMware.Hv.Helper/Json/Farm/ManualFarm.json @@ -7,17 +7,31 @@ "Description": "Manual PS Test", "Enabled": null, "Deleting": false, - "Settings": null, + "Settings": { + "DisconnectedSessionTimeoutPolicy" : "NEVER", + "DisconnectedSessionTimeoutMinutes" : 1, + "EmptySessionTimeoutPolicy" : "AFTER", + "EmptySessionTimeoutMinutes" : 1, + "LogoffAfterTimeout" : false + }, "Desktop": null, - "DisplayProtocolSettings": null, + "DisplayProtocolSettings": { + "DefaultDisplayProtocol" : "PCOIP", + "AllowDisplayProtocolOverride" : false, + "EnableHTMLAccess" : false + }, "ServerErrorThreshold": null, - "MirageConfigurationOverrides": null + "MirageConfigurationOverrides": { + "OverrideGlobalSetting" : false, + "Enabled" : false, + "Url" : null + } }, "AutomatedFarmSpec": null, "ManualFarmSpec": { "RdsServers": [ { - "rdsServer": "WIN-ORKA1Q8B0P7" + "rdsServer": "RDSServer.adviewdev.eng.vmware.com" } ] } diff --git a/Modules/VMware.Hv.Helper/New-HVPool/FullClone.json b/Modules/VMware.Hv.Helper/Json/Pool/FullClone.json similarity index 82% rename from Modules/VMware.Hv.Helper/New-HVPool/FullClone.json rename to Modules/VMware.Hv.Helper/Json/Pool/FullClone.json index 2ae9539..e6d7fae 100644 --- a/Modules/VMware.Hv.Helper/New-HVPool/FullClone.json +++ b/Modules/VMware.Hv.Helper/Json/Pool/FullClone.json @@ -5,7 +5,44 @@ "AccessGroup": "Root", "Description": "create full clone via JSON" }, - "DesktopSettings": null, + "DesktopSettings": { + "enabled": true, + "deleting": false, + "connectionServerRestrictions": null, + "logoffSettings": { + "powerPolicy": "TAKE_NO_POWER_ACTION", + "automaticLogoffPolicy": "NEVER", + "automaticLogoffMinutes": 120, + "allowUsersToResetMachines": false, + "allowMultipleSessionsPerUser": false, + "deleteOrRefreshMachineAfterLogoff": "NEVER", + "refreshOsDiskAfterLogoff": "NEVER", + "refreshPeriodDaysForReplicaOsDisk": 5, + "refreshThresholdPercentageForReplicaOsDisk": 10 + }, + "displayProtocolSettings": { + "supportedDisplayProtocols": ["PCOIP", "BLAST" ], + "defaultDisplayProtocol": "BLAST", + "allowUsersToChooseProtocol": true, + "pcoipDisplaySettings": { + "renderer3D": "DISABLED", + "enableGRIDvGPUs": false, + "vRamSizeMB": 96, + "maxNumberOfMonitors": 3, + "maxResolutionOfAnyOneMonitor": "WSXGA_PLUS" + }, + "enableHTMLAccess": true + }, + "flashSettings": { + "quality": "NO_CONTROL", + "throttling": "DISABLED" + }, + "mirageConfigurationOverrides": { + "overrideGlobalSetting": false, + "enabled": false, + "url": false + } + }, "Type": "AUTOMATED", "AutomatedDesktopSpec": { "ProvisioningType": "VIRTUAL_CENTER", @@ -69,7 +106,7 @@ "NoCustomizationSettings": { "DoNotPowerOnVMsAfterCreation": false }, - "SysprepCustomizationSettings": null, + "SysprepCustomizationSettings": {"customizationSpec" : "praveencust"}, "QuickprepCustomizationSettings": null, "CloneprepCustomizationSettings": null } @@ -77,6 +114,5 @@ "ManualDesktopSpec": null, "RdsDesktopSpec": null, "GlobalEntitlementData": null, - "NetBiosName" : "adviewdev", - "SysPrepName" : "praveencust" + "NetBiosName" : "adviewdev" } diff --git a/Modules/VMware.Hv.Helper/New-HVPool/InstantClone.json b/Modules/VMware.Hv.Helper/Json/Pool/InstantClone.json similarity index 88% rename from Modules/VMware.Hv.Helper/New-HVPool/InstantClone.json rename to Modules/VMware.Hv.Helper/Json/Pool/InstantClone.json index a8da482..4c3c584 100644 --- a/Modules/VMware.Hv.Helper/New-HVPool/InstantClone.json +++ b/Modules/VMware.Hv.Helper/Json/Pool/InstantClone.json @@ -5,7 +5,44 @@ "AccessGroup": "ROOT", "Description": "create instant pool" }, - "DesktopSettings": null, + "DesktopSettings": { + "enabled": true, + "deleting": false, + "connectionServerRestrictions": null, + "logoffSettings": { + "powerPolicy": "ALWAYS_POWERED_ON", + "automaticLogoffPolicy": "NEVER", + "automaticLogoffMinutes": 120, + "allowUsersToResetMachines": false, + "allowMultipleSessionsPerUser": false, + "deleteOrRefreshMachineAfterLogoff": "DELETE", + "refreshOsDiskAfterLogoff": "NEVER", + "refreshPeriodDaysForReplicaOsDisk": 5, + "refreshThresholdPercentageForReplicaOsDisk": 10 + }, + "displayProtocolSettings": { + "supportedDisplayProtocols": ["PCOIP", "BLAST" ], + "defaultDisplayProtocol": "BLAST", + "allowUsersToChooseProtocol": true, + "pcoipDisplaySettings": { + "renderer3D": "DISABLED", + "enableGRIDvGPUs": false, + "vRamSizeMB": 96, + "maxNumberOfMonitors": 3, + "maxResolutionOfAnyOneMonitor": "WSXGA_PLUS" + }, + "enableHTMLAccess": true + }, + "flashSettings": { + "quality": "NO_CONTROL", + "throttling": "DISABLED" + }, + "mirageConfigurationOverrides": { + "overrideGlobalSetting": false, + "enabled": false, + "url": false + } + }, "Type": "AUTOMATED", "AutomatedDesktopSpec": { "ProvisioningType": "INSTANT_CLONE_ENGINE", diff --git a/Modules/VMware.Hv.Helper/New-HVPool/LinkedClone.json b/Modules/VMware.Hv.Helper/Json/Pool/LinkedClone.json similarity index 80% rename from Modules/VMware.Hv.Helper/New-HVPool/LinkedClone.json rename to Modules/VMware.Hv.Helper/Json/Pool/LinkedClone.json index 53171a6..ee3dfac 100644 --- a/Modules/VMware.Hv.Helper/New-HVPool/LinkedClone.json +++ b/Modules/VMware.Hv.Helper/Json/Pool/LinkedClone.json @@ -5,7 +5,44 @@ "AccessGroup": "Root", "Description": "created linkedclone pool from ps" }, - "DesktopSettings": null, + "DesktopSettings": { + "enabled": true, + "deleting": false, + "connectionServerRestrictions": null, + "logoffSettings": { + "powerPolicy": "TAKE_NO_POWER_ACTION", + "automaticLogoffPolicy": "NEVER", + "automaticLogoffMinutes": 120, + "allowUsersToResetMachines": false, + "allowMultipleSessionsPerUser": false, + "deleteOrRefreshMachineAfterLogoff": "NEVER", + "refreshOsDiskAfterLogoff": "NEVER", + "refreshPeriodDaysForReplicaOsDisk": 5, + "refreshThresholdPercentageForReplicaOsDisk": 10 + }, + "displayProtocolSettings": { + "supportedDisplayProtocols": ["RDP","PCOIP", "BLAST" ], + "defaultDisplayProtocol": "PCOIP", + "allowUsersToChooseProtocol": true, + "pcoipDisplaySettings": { + "renderer3D": "DISABLED", + "enableGRIDvGPUs": false, + "vRamSizeMB": 96, + "maxNumberOfMonitors": 3, + "maxResolutionOfAnyOneMonitor": "WSXGA_PLUS" + }, + "enableHTMLAccess": true + }, + "flashSettings": { + "quality": "NO_CONTROL", + "throttling": "DISABLED" + }, + "mirageConfigurationOverrides": { + "overrideGlobalSetting": false, + "enabled": false, + "url": null + } + }, "Type": "AUTOMATED", "AutomatedDesktopSpec": { "ProvisioningType": "VIEW_COMPOSER", @@ -33,7 +70,7 @@ "Template": null, "ParentVm": "Agent_pra", "Snapshot": "kb-hotfix", - "Datacenter": null, + "Datacenter": "Dev-Dc", "VmFolder": "Praveen", "HostOrCluster": "CS-1", "ResourcePool": "CS-1" @@ -52,7 +89,8 @@ "UseNativeSnapshots": false, "SpaceReclamationSettings": { "ReclaimVmDiskSpace": false, - "ReclamationThresholdGB": null + "ReclamationThresholdGB": null, + "BlackoutTimes" : null }, "PersistentDiskSettings": { "RedirectWindowsProfile": false, @@ -75,19 +113,31 @@ } }, "VirtualCenterNetworkingSettings": { - "Nics": null + "Nics": [ + { + "Nic": "nicName", + "NetworkLabelAssignmentSpecs": [ + { + "Enabled" : false, + "networkLabel" : null, + "maxLabelType" : null, + "maxLabel" : null + } + ] + } + ] } }, "VirtualCenterManagedCommonSettings": { "TransparentPageSharingScope": "VM" }, "CustomizationSettings": { - "CustomizationType": "QUICK_PREP", - "DomainAdministrator": null, + "CustomizationType": "SYS_PREP", + "DomainAdministrator": "administrator", "AdContainer": "CN=Computers", "ReusePreExistingAccounts": false, "NoCustomizationSettings": null, - "SysprepCustomizationSettings": null, + "SysprepCustomizationSettings": {"customizationSpec" : "praveencust"}, "QuickprepCustomizationSettings": { "PowerOffScriptName": null, "PowerOffScriptParameters": null, @@ -99,7 +149,6 @@ }, "ManualDesktopSpec": null, "RdsDesktopSpec": null, - "GlobalEntitlementData": null, - "NetBiosName" : "adviewdev", - "SysPrepName" : "praveencust" + "GlobalEntitlementData": null, + "NetBiosName" : "adviewdev" } diff --git a/Modules/VMware.Hv.Helper/New-HVPool/ManualSpec.json b/Modules/VMware.Hv.Helper/Json/Pool/ManualSpec.json similarity index 54% rename from Modules/VMware.Hv.Helper/New-HVPool/ManualSpec.json rename to Modules/VMware.Hv.Helper/Json/Pool/ManualSpec.json index b302bba..8b95389 100644 --- a/Modules/VMware.Hv.Helper/New-HVPool/ManualSpec.json +++ b/Modules/VMware.Hv.Helper/Json/Pool/ManualSpec.json @@ -5,7 +5,44 @@ "AccessGroup": "ROOT", "Description": "Manual pool creation" }, - "DesktopSettings": null, + "DesktopSettings": { + "enabled": true, + "deleting": false, + "connectionServerRestrictions": null, + "logoffSettings": { + "powerPolicy": "TAKE_NO_POWER_ACTION", + "automaticLogoffPolicy": "NEVER", + "automaticLogoffMinutes": 120, + "allowUsersToResetMachines": false, + "allowMultipleSessionsPerUser": false, + "deleteOrRefreshMachineAfterLogoff": "NEVER", + "refreshOsDiskAfterLogoff": "NEVER", + "refreshPeriodDaysForReplicaOsDisk": 5, + "refreshThresholdPercentageForReplicaOsDisk": 10 + }, + "displayProtocolSettings": { + "supportedDisplayProtocols": ["PCOIP", "BLAST" ], + "defaultDisplayProtocol": "BLAST", + "allowUsersToChooseProtocol": true, + "pcoipDisplaySettings": { + "renderer3D": "DISABLED", + "enableGRIDvGPUs": false, + "vRamSizeMB": 96, + "maxNumberOfMonitors": 3, + "maxResolutionOfAnyOneMonitor": "WSXGA_PLUS" + }, + "enableHTMLAccess": true + }, + "flashSettings": { + "quality": "NO_CONTROL", + "throttling": "DISABLED" + }, + "mirageConfigurationOverrides": { + "overrideGlobalSetting": false, + "enabled": false, + "url": false + } + }, "Type": "MANUAL", "AutomatedDesktopSpec": null, "ManualDesktopSpec": { @@ -16,7 +53,7 @@ "Source": "VIRTUAL_CENTER", "Machines": [ { - "Machine" : "PowerCLI-VM" + "Machine" : "Praveen_Agent" } ], "VirtualCenter": null, @@ -32,4 +69,5 @@ }, "RdsDesktopSpec": null, "GlobalEntitlementData": null + } diff --git a/Modules/VMware.Hv.Helper/Json/Pool/RdsSpec.json b/Modules/VMware.Hv.Helper/Json/Pool/RdsSpec.json new file mode 100644 index 0000000..bab0c67 --- /dev/null +++ b/Modules/VMware.Hv.Helper/Json/Pool/RdsSpec.json @@ -0,0 +1,27 @@ +{ + "Base": { + "Name" : "RdsJson", + "DisplayName": "TestRDSPS", + "AccessGroup": "Root", + "Description": "Testing PS" + }, + "DesktopSettings": { + "enabled": true, + "deleting": false, + "connectionServerRestrictions": null, + "logoffSettings": null, + "displayProtocolSettings": null, + "flashSettings": { + "quality": "NO_CONTROL", + "throttling": "DISABLED" + }, + "mirageConfigurationOverrides": null + }, + "Type": "RDS", + "AutomatedDesktopSpec": null, + "ManualDesktopSpec": null, + "RdsDesktopSpec": { + "Farm": "test1" + }, + "GlobalEntitlementData": null +} diff --git a/Modules/VMware.Hv.Helper/New-HVPool/RdsSpec.json b/Modules/VMware.Hv.Helper/New-HVPool/RdsSpec.json deleted file mode 100644 index 86b3571..0000000 --- a/Modules/VMware.Hv.Helper/New-HVPool/RdsSpec.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "Base": { - "Name" : "RdsJson", - "DisplayName": "TestRDSPS", - "AccessGroup": "Root", - "Description": "Testing PS" - }, - "DesktopSettings": null, - "Type": "RDS", - "AutomatedDesktopSpec": null, - "ManualDesktopSpec": null, - "RdsDesktopSpec": { - "Farm": "Farm2" - }, - "GlobalEntitlementData": null -} diff --git a/Modules/VMware.Hv.Helper/README.md b/Modules/VMware.Hv.Helper/README.md new file mode 100644 index 0000000..fe87153 --- /dev/null +++ b/Modules/VMware.Hv.Helper/README.md @@ -0,0 +1,20 @@ +Prerequisites/Steps to use this module: + +1. This module only works for Horizon product E.g. Horizon 7.0.2 and later. +2. Install the latest version of Powershell, PowerCLI(6.5) or (later version via psgallery). +3. Import HorizonView module by running: Import-Module VMware.VimAutomation.HorizonView. +4. Import "VMware.Hv.Helper" module by running: Import-Module -Name "location of this module" or Get-Module -ListAvailable 'VMware.Hv.Helper' | Import-Module. +5. Get-Command -Module "This module Name" to list all available functions or Get-Command -Module 'VMware.Hv.Helper'. + +# Example script to connect view API service of Connection Server: + +Import-Module VMware.VimAutomation.HorizonView +# Connection to view API service +$hvServer = Connect-HVServer -server +$hvServices = $hvserver.ExtensionData +$csList = $hvServices.ConnectionServer.ConnectionServer_List() +# Load this module +Get-Module -ListAvailable 'VMware.Hv.Helper' | Import-Module +Get-Command -Module 'VMware.Hv.Helper' +# Use advanced functions of this module +New-HVPool -spec 'path to InstantClone.json file' diff --git a/Modules/VMware.Hv.Helper/Set-HVGlobalSettings/Set-HVGlobalSettings.json b/Modules/VMware.Hv.Helper/Set-HVGlobalSettings/Set-HVGlobalSettings.json new file mode 100644 index 0000000..269d034 --- /dev/null +++ b/Modules/VMware.Hv.Helper/Set-HVGlobalSettings/Set-HVGlobalSettings.json @@ -0,0 +1,22 @@ +{ + "generalData.clientMaxSessionTimePolicy": "TIMEOUT_AFTER", + "generalData.clientMaxSessionTimeMinutes": 600, + "generalData.clientIdleSessionTimeoutPolicy": "NEVER", + "generalData.clientIdleSessionTimeoutMinutes": null, + "generalData.clientSessionTimeoutMinutes": 1200, + "generalData.desktopSSOTimeoutPolicy": "DISABLE_AFTER", + "generalData.desktopSSOTimeoutMinutes": 15, + "generalData.applicationSSOTimeoutPolicy": "ALWAYS_ENABLED", + "generalData.applicationSSOTimeoutMinutes": null, + "generalData.viewAPISessionTimeoutMinutes": 10, + "generalData.preLoginMessage": null, + "generalData.displayWarningBeforeForcedLogoff": true, + "generalData.forcedLogoffTimeoutMinutes": 5, + "generalData.forcedLogoffMessage": "Your desktop is scheduled for an important update and will shut down in 5 minutes. Please save any unsaved work now", + "generalData.enableServerInSingleUserMode": false, + "generalData.storeCALOnBroker": false, + "generalData.storeCALOnClient": false, + "securityData.reauthSecureTunnelAfterInterruption": true, + "securityData.messageSecurityMode": "ENHANCED", + "securityData.enableIPSecForSecurityServerPairing": true +} \ No newline at end of file diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.format.ps1xml b/Modules/VMware.Hv.Helper/VMware.HV.Helper.format.ps1xml index 62ce001..7f8a6aa 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.format.ps1xml +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.format.ps1xml @@ -27,6 +27,10 @@ 16 + + + 8 + 7 @@ -56,10 +60,23 @@ $_.desktopSummaryData.userAssignment - + + + $filterContains = Get-HVQueryFilter localData.desktops -contains ([VMware.Hv.DesktopId[]]$_.id) + $GlobalfilterContains = Get-HVQueryFilter localData.desktops -contains ([VMware.Hv.DesktopId[]]$_.id) + Try { + $results += Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $filterContains + $results += Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $GlobalfilterContains + } Catch { + #Do nothing + } + $results.length + + + $_.desktopSummaryData.enabled - + $_.desktopSummaryData.numSessions @@ -97,6 +114,20 @@ $_.desktopSummaryData.userAssignment + + + $filterContains = Get-HVQueryFilter localData.desktops -contains ([VMware.Hv.DesktopId[]]$_.id) + $GlobalfilterContains = Get-HVQueryFilter localData.desktops -contains ([VMware.Hv.DesktopId[]]$_.id) + Try { + $results += Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $filterContains + $results += Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $GlobalfilterContains + } Catch { + #Do nothing + } + $results.length + + + $_.desktopSummaryData.enabled diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psd1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psd1 index 705cc80..42dc6aa 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psd1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psd1 @@ -12,7 +12,7 @@ # RootModule = '' # Version number of this module. -ModuleVersion = '1.0' +ModuleVersion = '1.1' # ID used to uniquely identify this module GUID = '6d3f7fb5-4e52-43d8-91e1-f65f72532a1d' diff --git a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 index 7c06a99..c58e1c0 100644 --- a/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 +++ b/Modules/VMware.Hv.Helper/VMware.HV.Helper.psm1 @@ -1,5 +1,5 @@ #Script Module : VMware.Hv.Helper -#Version : 1.0 +#Version : 1.2 #Copyright © 2016 VMware, Inc. All Rights Reserved. @@ -48,14 +48,23 @@ function Get-ViewAPIService { return $hvServer.ExtensionData } } elseif ($global:DefaultHVServers.Length -gt 0) { - if ($pscmdlet.ShouldProcess($global:DefaultHVServers[0].uid,'hvServer not specified, use default hvServer connection?')) { - $hvServer = $global:DefaultHVServers[0] - return $hvServer.ExtensionData - } + $hvServer = $global:DefaultHVServers[0] + return $hvServer.ExtensionData } return $null } +function Get-HVConfirmFlag { + Param( + [Parameter(Mandatory = $true)] + $keys + ) + if (($keys -contains 'Confirm') -or ($keys -contains 'WhatIf')) { + return $true + } + return $false +} + function Get-VcenterID { param( [Parameter(Mandatory = $true)] @@ -177,30 +186,30 @@ The Add-HVDesktop adds virtual machines to already exiting pools by using view A View API service object of Connect-HVServer cmdlet. .EXAMPLE + Add-HVDesktop -PoolName 'ManualPool' -Machines 'manualPool1', 'manualPool2' -Confirm:$false Add managed manual VMs to existing manual pool - Add-HVDesktop -PoolName 'ManualPool' -Machines 'manualPool1', 'manualPool2'. .EXAMPLE - Add virtual machines to automated specific named dedicated pool Add-HVDesktop -PoolName 'SpecificNamed' -Machines 'vm-01', 'vm-02' -Users 'user1', 'user2' + Add virtual machines to automated specific named dedicated pool .EXAMPLE - Add machines to automated specific named Floating pool Add-HVDesktop -PoolName 'SpecificNamed' -Machines 'vm-03', 'vm-04' + Add machines to automated specific named Floating pool .EXAMPLE - Add machines to unmanged manual pool Add-HVDesktop -PoolName 'Unmanaged' -Machines 'desktop-1.eng.vmware.com' + Add machines to unmanged manual pool .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 Dependencies : Make sure pool already exists before adding VMs to it. ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -239,6 +248,7 @@ The Add-HVDesktop adds virtual machines to already exiting pools by using view A } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys try { $desktopPool = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer } catch { @@ -296,7 +306,10 @@ The Add-HVDesktop adds virtual machines to already exiting pools by using view A return } } - $desktop_service_helper.Desktop_AddMachinesToManualDesktop($services,$id,$machineList) + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($machines)) { + $desktop_service_helper.Desktop_AddMachinesToManualDesktop($services,$id,$machineList) + } + write-host "Successfully added desktop(s) to pool" } default { Write-Error "Only Automated/Manual pool types support this add operation" @@ -347,6 +360,7 @@ function Get-MachinesByVCenter ($MachineList,$VcId) { } return $machines } + function Add-HVRDSServer { <# .SYNOPSIS @@ -365,21 +379,21 @@ function Add-HVRDSServer { View API service object of Connect-HVServer cmdlet. .EXAMPLE + Add-HVRDSServer -Farm "manualFarmTest" -RdsServers "vm-for-rds","vm-for-rds-2" -Confirm:$false Add RDSServers to manual farm - Add-HVRDSServer -Farm "manualFarmTest" -RdsServers "vm-for-rds","vm-for-rds-2" .OUTPUTS None .NOTES - Author : Ankit Gupta. - Author email : guptaa@vmware.com - Version : 1.0 + Author : praveen mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 Dependencies : Make sure farm already exists before adding RDSServers to it. ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> [CmdletBinding( @@ -407,10 +421,11 @@ function Add-HVRDSServer { } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys try { - $farmSpecObj = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer + $farmSpecObj = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer -suppressInfo $true } catch { - Write-Error "Make sure Get-HVFarm advanced function is loaded, $_" + Write-Error "Make sure Get-HVFarmSummary advanced function is loaded, $_" break } if ($farmSpecObj) { @@ -430,7 +445,10 @@ function Add-HVRDSServer { 'MANUAL' { try { $serverList = Get-RegisteredRDSServer -services $services -serverList $rdsServers - $farm_service_helper.Farm_AddRDSServers($services, $id, $serverList) + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($rdsServers)) { + $farm_service_helper.Farm_AddRDSServers($services, $id, $serverList) + } + write-host "Successfully added RDS Server(s) to Farm" } catch { Write-Error "Failed to Add RDS Server to Farm with error: $_" break @@ -443,7 +461,8 @@ function Add-HVRDSServer { [System.gc]::collect() } } -[System.Reflection.Assembly]::LoadWithPartialName("System.Data.OracleClient") | Out-Null + + function Connect-HVEvent { <# @@ -464,21 +483,21 @@ function Connect-HVEvent { Password corresponds to 'dbUserName' user. .EXAMPLE - Connecting to the database with default username configured on Connection Server $hvServer. Connect-HVEvent -HvServer $hvServer + Connecting to the database with default username configured on Connection Server $hvServer. .EXAMPLE - Connecting to the database configured on Connection Server $hvServer with customised user name 'system'. $hvDbServer = Connect-HVEvent -HvServer $hvServer -DbUserName 'system' + Connecting to the database configured on Connection Server $hvServer with customised user name 'system'. .EXAMPLE - Connecting to the database with customised user name and password. $hvDbServer = Connect-HVEvent -HvServer $hvServer -DbUserName 'system' -DbPassword 'censored' + Connecting to the database with customised user name and password. .EXAMPLE + C:\PS>$password = Read-Host 'Database Password' -AsSecureString + C:\PS>$hvDbServer = Connect-HVEvent -HvServer $hvServer -DbUserName 'system' -DbPassword $password Connecting to the database with customised user name and password, with password being a SecureString. - $password = Read-Host 'Database Password' -AsSecureString - $hvDbServer = Connect-HVEvent -HvServer $hvServer -DbUserName 'system' -DbPassword $password .OUTPUTS Returns a custom object that has database connection as 'dbConnection' property. @@ -486,11 +505,11 @@ function Connect-HVEvent { .NOTES Author : Paramesh Oddepally. Author email : poddepally@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> [CmdletBinding()] @@ -506,6 +525,7 @@ function Connect-HVEvent { ) begin { + [System.Reflection.Assembly]::LoadWithPartialName("System.Data.OracleClient") | Out-Null # Connect to Connection Server and call the View API service $services = Get-ViewAPIService -hvServer $hvServer if ($null -eq $services) { @@ -594,8 +614,8 @@ function Disconnect-HVEvent { Connection object returned by Connect-HVEvent advanced function. This is a mandatory input. .EXAMPLE - Disconnecting the database connection on $hvDbServer. Disconnect-HVEvent -HvDbServer $hvDbServer + Disconnecting the database connection on $hvDbServer. .OUTPUTS None @@ -603,11 +623,11 @@ function Disconnect-HVEvent { .NOTES Author : Paramesh Oddepally. Author email : poddepally@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -684,15 +704,15 @@ function Get-HVEvent { String that can applied in filtering on 'Message' column. .EXAMPLE + C:\PS>$e = Get-HVEvent -hvDbServer $hvDbServer + C:\PS>$e.Events Querying all the database events on database $hvDbServer. - $e = Get-HVEvent -hvDbServer $hvDbServer - $e.Events .EXAMPLE + C:\PS>$e = Get-HVEvent -HvDbServer $hvDbServer -TimePeriod 'all' -FilterType 'startsWith' -UserFilter 'aduser' -SeverityFilter 'err' -TimeFilter 'HH:MM:SS.fff' -ModuleFilter 'broker' -MessageFilter 'aduser' + C:\PS>$e.Events | Export-Csv -Path 'myEvents.csv' -NoTypeInformation Querying all the database events where user name startswith 'aduser', severity is of 'err' type, having module name as 'broker', message starting with 'aduser' and time starting with 'HH:MM:SS.fff'. The resulting events will be exported to a csv file 'myEvents.csv'. - $e = Get-HVEvent -HvDbServer $hvDbServer -TimePeriod 'all' -FilterType 'startsWith' -UserFilter 'aduser' -SeverityFilter 'err' -TimeFilter 'HH:MM:SS.fff' -ModuleFilter 'broker' -MessageFilter 'aduser' - $e.Events | Export-Csv -Path 'myEvents.csv' -NoTypeInformation .OUTPUTS Returns a custom object that has events information in 'Events' property. Events property will have events information with five columns: UserName, Severity, EventTime, Module and Message. @@ -700,11 +720,11 @@ function Get-HVEvent { .NOTES Author : Paramesh Oddepally. Author email : poddepally@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -904,38 +924,43 @@ function Get-HVFarm { .PARAMETER Enabled search for farms which are enabled -.PARAMETER Full - Switch to get list of FarmSummaryView or FarmInfo objects in the result. If it is true a list of FarmInfo objects is returned ohterwise a list of FarmSummaryView objects is returned. +.PARAMETER SuppressInfo + Suppress text info, when no farm found with given search parameters .PARAMETER HvServer - Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. + Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE Get-HVFarm -FarmName 'Farm-01' + Queries and returns farmInfo based on given parameter farmName .EXAMPLE Get-HVFarm -FarmName 'Farm-01' -FarmDisplayName 'Sales RDS Farm' + Queries and returns farmInfo based on given parameters farmName, farmDisplayName .EXAMPLE Get-HVFarm -FarmName 'Farm-01' -FarmType 'MANUAL' + Queries and returns farmInfo based on given parameters farmName, farmType .EXAMPLE Get-HVFarm -FarmName 'Farm-01' -FarmType 'MANUAL' -Enabled $true + Queries and returns farmInfo based on given parameters farmName, FarmType etc .EXAMPLE - Get-HVFarm -FarmName 'Farm-01' + Get-HVFarm -FarmName 'Farm-0*' + Queries and returns farmInfo based on parameter farmName with wild character * .OUTPUTs - Returns the list of FarmSummaryView or FarmInfo object matching the query criteria. + Returns the list of FarmInfo object matching the query criteria. .NOTES - Author : Ankit Gupta. - Author email : guptaa@vmware.com - Version : 1.0 + Author : praveen mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -962,6 +987,10 @@ function Get-HVFarm { [boolean] $Enabled, + [Parameter(Mandatory = $false)] + [boolean] + $SuppressInfo = $false, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -972,6 +1001,12 @@ function Get-HVFarm { break } $farmList = Find-HVFarm -Param $PSBoundParameters + if (! $farmList) { + if (! $SuppressInfo) { + Write-Host "Get-HVFarm: No Farm Found with given search parameters" + } + return $farmList + } $farm_service_helper = New-Object VMware.Hv.FarmService $queryResults = @() foreach ($id in $farmList.id) { @@ -991,48 +1026,56 @@ function Get-HVFarmSummary { This function queries the specified Connection Server for farms which are configured on the server. If no farm is configured on the specified connection server or no farm matches the given search criteria, it will return null. .PARAMETER FarmName - farmName to be searched + FarmName to be searched .PARAMETER FarmDisplayName - farmDisplayName to be searched + FarmDisplayName to be searched .PARAMETER FarmType - farmType to be searched. It can take following values: + FarmType to be searched. It can take following values: "AUTOMATED" - search for automated farms only 'MANUAL' - search for manual farms only .PARAMETER Enabled - search for farms which are enabled + Search for farms which are enabled + +.PARAMETER SuppressInfo + Suppress text info, when no farm found with given search parameters .PARAMETER HvServer - Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. + Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE - Get-HVFarm -FarmName 'Farm-01' + Get-HVFarmSummary -FarmName 'Farm-01' + Queries and returns farmSummary objects based on given parameter farmName .EXAMPLE - Get-HVFarm -FarmName 'Farm-01' -FarmDisplayName 'Sales RDS Farm' + Get-HVFarmSummary -FarmName 'Farm-01' -FarmDisplayName 'Sales RDS Farm' + Queries and returns farmSummary objects based on given parameters farmName, farmDisplayName .EXAMPLE - Get-HVFarm -FarmName 'Farm-01' -FarmType 'MANUAL' + Get-HVFarmSummary -FarmName 'Farm-01' -FarmType 'MANUAL' + Queries and returns farmSummary objects based on given parameters farmName, farmType .EXAMPLE - Get-HVFarm -FarmName 'Farm-01' -FarmType 'MANUAL' -Enabled $true + Get-HVFarmSummary -FarmName 'Farm-01' -FarmType 'MANUAL' -Enabled $true + Queries and returns farmSummary objects based on given parameters farmName, FarmType etc .EXAMPLE - Get-HVFarm -FarmName 'Farm-01' + Get-HVFarmSummary -FarmName 'Farm-0*' + Queries and returns farmSummary objects based on given parameter farmName with wild character * .OUTPUTs - Returns the list of FarmSummaryView or FarmInfo object matching the query criteria. + Returns the list of FarmSummary object matching the query criteria. .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -1059,6 +1102,10 @@ function Get-HVFarmSummary { [boolean] $Enabled, + [Parameter(Mandatory = $false)] + [boolean] + $SuppressInfo = $false, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -1069,7 +1116,10 @@ function Get-HVFarmSummary { break } $farmList = Find-HVFarm -Param $PSBoundParameters - return $farmList + if (!$farmList -and !$SuppressInfo) { + Write-Host "Get-HVFarmSummary: No Farm Found with given search parameters" + } + Return $farmList } function Find-HVFarm { @@ -1092,27 +1142,51 @@ function Find-HVFarm { $query_service_helper = New-Object VMware.Hv.QueryServiceService $query = New-Object VMware.Hv.QueryDefinition + $wildcard = $false # build the query values + if ($params['FarmName'] -and $params['FarmName'].contains('*')) { + $wildcard = $true + } + if ($params['FarmDisplayName'] -and $params['FarmDisplayName'].contains('*')) { + $wildcard = $true + } $query.queryEntityType = 'FarmSummaryView' - [VMware.Hv.queryfilter[]]$filterSet = @() - foreach ($setting in $farmSelectors.Keys) { - if ($null -ne $params[$setting]) { - $equalsFilter = New-Object VMware.Hv.QueryFilterEquals - $equalsFilter.memberName = $farmSelectors[$setting] - $equalsFilter.value = $params[$setting] - $filterSet += $equalsFilter + if (! $wildcard) { + [VMware.Hv.queryfilter[]]$filterSet = @() + foreach ($setting in $farmSelectors.Keys) { + if ($null -ne $params[$setting]) { + $equalsFilter = New-Object VMware.Hv.QueryFilterEquals + $equalsFilter.memberName = $farmSelectors[$setting] + $equalsFilter.value = $params[$setting] + $filterSet += $equalsFilter + } + } + if ($filterSet.Count -gt 0) { + $queryList = New-Object VMware.Hv.QueryFilterAnd + $queryList.Filters = $filterset + $query.Filter = $queryList } - } - if ($filterSet.Count -gt 0) { - $queryList = New-Object VMware.Hv.QueryFilterAnd - $queryList.Filters = $filterset - $query.Filter = $queryList - } - $queryResults = $query_service_helper.QueryService_Query($services, $query) - $farmList = $queryResults.results - + $queryResults = $query_service_helper.QueryService_Query($services, $query) + $farmList = $queryResults.results + } elseif ($wildcard -or [string]::IsNullOrEmpty($farmList)){ + $query.Filter = $null + $queryResults = $query_service_helper.QueryService_Query($services,$query) + $strFilterSet = @() + foreach ($setting in $farmSelectors.Keys) { + if ($null -ne $params[$setting]) { + if ($wildcard -and (($setting -eq 'FarmName') -or ($setting -eq 'FarmDisplayName')) ) { + $strFilterSet += '($_.' + $farmSelectors[$setting] + ' -like "' + $params[$setting] + '")' + } else { + $strFilterSet += '($_.' + $farmSelectors[$setting] + ' -eq "' + $params[$setting] + '")' + } + } + } + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $farmList = $queryResults.results | where $scriptBlock + } Return $farmList } @@ -1156,33 +1230,40 @@ function Get-HVPool { If the value is true then only pools which are enabled would be returned. If the value is false then only pools which are disabled would be returned. +.PARAMETER SuppressInfo + Suppress text info, when no pool found with given search parameters + .PARAMETER HvServer Reference to Horizon View Server to query the pools from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Get-HVPool -PoolName 'mypool' -PoolType MANUAL -UserAssignment FLOATING -Enabled $true -ProvisioningEnabled $true + Queries and returns pool object(s) based on given parameters poolName, poolType etc. .EXAMPLE Get-HVPool -PoolType AUTOMATED -UserAssignment FLOATING + Queries and returns pool object(s) based on given parameters poolType and userAssignment .EXAMPLE Get-HVPool -PoolName 'myrds' -PoolType RDS -UserAssignment DEDICATED -Enabled $false + Queries and returns pool object(s) based on given parameters poolName, PoolType etc. .EXAMPLE Get-HVPool -PoolName 'myrds' -PoolType RDS -UserAssignment DEDICATED -Enabled $false -HvServer $mycs + Queries and returns pool object(s) based on given parameters poolName and HvServer etc. .OUTPUTS - Returns list of objects of type Desktop + Returns list of objects of type DesktopInfo .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -1218,6 +1299,10 @@ function Get-HVPool { [boolean] $ProvisioningEnabled, + [Parameter(Mandatory = $false)] + [boolean] + $SuppressInfo = $false, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -1228,6 +1313,12 @@ function Get-HVPool { break } $poolList = Find-HVPool -Param $PSBoundParameters + if (! $poolList) { + if (! $SuppressInfo) { + Write-Host "Get-HVPool: No Pool Found with given search parameters" + } + return $poolList + } $queryResults = @() $desktop_helper = New-Object VMware.Hv.DesktopService foreach ($id in $poolList.id) { @@ -1278,21 +1369,28 @@ function Get-HVPoolSummary { If the value is true then only pools which are enabled would be returned. If the value is false then only pools which are disabled would be returned. +.PARAMETER SuppressInfo + Suppress text info, when no pool found with given search parameters + .PARAMETER HvServer Reference to Horizon View Server to query the pools from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Get-HVPoolSummary -PoolName 'mypool' -PoolType MANUAL -UserAssignment FLOATING -Enabled $true -ProvisioningEnabled $true + Queries and returns desktopSummaryView based on given parameters poolName, poolType etc. .EXAMPLE Get-HVPoolSummary -PoolType AUTOMATED -UserAssignment FLOATING + Queries and returns desktopSummaryView based on given parameters poolType, userAssignment. .EXAMPLE Get-HVPoolSummary -PoolName 'myrds' -PoolType RDS -UserAssignment DEDICATED -Enabled $false + Queries and returns desktopSummaryView based on given parameters poolName, poolType, userAssignment etc. .EXAMPLE Get-HVPoolSummary -PoolName 'myrds' -PoolType RDS -UserAssignment DEDICATED -Enabled $false -HvServer $mycs + Queries and returns desktopSummaryView based on given parameters poolName, HvServer etc. .OUTPUTS Returns list of DesktopSummaryView @@ -1300,11 +1398,11 @@ function Get-HVPoolSummary { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -1340,6 +1438,10 @@ function Get-HVPoolSummary { [boolean] $ProvisioningEnabled, + [Parameter(Mandatory = $false)] + [boolean] + $SuppressInfo = $false, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -1349,8 +1451,11 @@ function Get-HVPoolSummary { Write-Error "Could not retrieve ViewApi services from connection object" break } - $poolList = Find-HVPool -Param $psboundparameters - Return $poolList + $pool_list = Find-HVPool -Param $psboundparameters + if (!$pool_list -and !$suppressInfo) { + Write-Host "Get-HVPoolSummary: No Pool Found with given search parameters" + } + Return $pool_list } function Find-HVPool { @@ -1468,32 +1573,40 @@ function Get-HVQueryFilter { .EXAMPLE Get-HVQueryFilter data.name -Eq vmware + Creates queryFilterEquals with given parameters memberName(position 0) and memberValue(position 2) .EXAMPLE Get-HVQueryFilter -MemberName data.name -Eq -MemberValue vmware + Creates queryFilterEquals with given parameters memberName and memberValue .EXAMPLE Get-HVQueryFilter data.name -Ne vmware + Creates queryFilterNotEquals filter with given parameters memberName and memberValue .EXAMPLE Get-HVQueryFilter data.name -Contains vmware + Creates queryFilterContains with given parameters memberName and memberValue .EXAMPLE Get-HVQueryFilter data.name -Startswith vmware + Creates queryFilterStartsWith with given parameters memberName and memberValue .EXAMPLE - $filter = Get-HVQueryFilter data.name -Startswith vmware - Get-HVQueryFilter -Not $filter + C:\PS>$filter = Get-HVQueryFilter data.name -Startswith vmware + C:\PS>Get-HVQueryFilter -Not $filter + Creates queryFilterNot with given parameter filter .EXAMPLE - $filter1 = Get-HVQueryFilter data.name -Startswith vmware - $filter2 = Get-HVQueryFilter data.name -Contains pool - Get-HVQueryFilter -And @($filter1, $filter2) + C:\PS>$filter1 = Get-HVQueryFilter data.name -Startswith vmware + C:\PS>$filter2 = Get-HVQueryFilter data.name -Contains pool + C:\PS>Get-HVQueryFilter -And @($filter1, $filter2) + Creates queryFilterAnd with given parameter filters array .EXAMPLE - $filter1 = Get-HVQueryFilter data.name -Startswith vmware - $filter2 = Get-HVQueryFilter data.name -Contains pool - Get-HVQueryFilter -Or @($filter1, $filter2) + C:\PS>$filter1 = Get-HVQueryFilter data.name -Startswith vmware + C:\PS>$filter2 = Get-HVQueryFilter data.name -Contains pool + C:\PS>Get-HVQueryFilter -Or @($filter1, $filter2) + Creates queryFilterOr with given parameter filters array .OUTPUTS Returns the QueryFilter object @@ -1501,11 +1614,11 @@ function Get-HVQueryFilter { .NOTES Author : Kummara Ramamohan. Author email : kramamohan@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> [CmdletBinding()] @@ -1616,26 +1729,28 @@ function Get-HVQueryResult { .PARAMETER HvServer Reference to Horizon View Server to query the data from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE Get-HVQueryResult DesktopSummaryView + Returns query results of entityType DesktopSummaryView(position 0) .EXAMPLE Get-HVQueryResult DesktopSummaryView (Get-HVQueryFilter data.name -Eq vmware) + Returns query results of entityType DesktopSummaryView(position 0) with given filter(position 1) .EXAMPLE Get-HVQueryResult -EntityType DesktopSummaryView -Filter (Get-HVQueryFilter desktopSummaryData.name -Eq vmware) + Returns query results of entityType DesktopSummaryView with given filter .EXAMPLE - Get-HVQueryResult -EntityType DesktopSummaryView -Filter (Get-HVQueryFilter desktopSummaryData.name -Eq vmware) -SortBy desktopSummaryData.displayName - -.EXAMPLE - $myFilter = Get-HVQueryFilter data.name -Contains vmware - Get-HVQueryResult -EntityType DesktopSummaryView -Filter $myFilter -SortBy desktopSummaryData.displayName -SortDescending $false + C:\PS>$myFilter = Get-HVQueryFilter data.name -Contains vmware + C:\PS>Get-HVQueryResult -EntityType DesktopSummaryView -Filter $myFilter -SortBy desktopSummaryData.displayName -SortDescending $false + Returns query results of entityType DesktopSummaryView with given filter and also sorted based on dispalyName .EXAMPLE Get-HVQueryResult DesktopSummaryView -Limit 10 + Returns query results of entityType DesktopSummaryView, maximum count equal to limit .OUTPUTS Returns the list of objects of entityType @@ -1643,11 +1758,11 @@ function Get-HVQueryResult { .NOTES Author : Kummara Ramamohan. Author email : kramamohan@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0,7.4 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1, PowerCLI 10.1.1 PowerShell Version : 5.0 #> @@ -1660,7 +1775,7 @@ function Get-HVQueryResult { 'FarmSummaryView','GlobalApplicationEntitlementInfo','GlobalEntitlementSummaryView', 'MachineNamesView','MachineSummaryView','PersistentDiskInfo','PodAssignmentInfo', 'RDSServerInfo','RDSServerSummaryView','RegisteredPhysicalMachineInfo','SampleInfo', - 'SessionLocalSummaryView','TaskInfo','URLRedirectionInfo','UserHomeSiteInfo')] + 'SessionLocalSummaryView','TaskInfo','URLRedirectionInfo','UserHomeSiteInfo','EventSummaryView','GlobalApplicationEntitlementInfo')] [string]$EntityType, [Parameter(Position = 1,Mandatory = $false)] @@ -1723,6 +1838,7 @@ function Get-HVQueryResult { } } + function New-HVFarm { <# .Synopsis @@ -1734,6 +1850,9 @@ function New-HVFarm { .PARAMETER LinkedClone Switch to Create Automated Linked Clone farm. +.PARAMETER InstantClone + Switch to Create Automated Instant Clone farm. + .PARAMETER Manual Switch to Create Manual farm. @@ -1758,38 +1877,38 @@ function New-HVFarm { .PARAMETER ParentVM Base image VM for RDS Servers. - Applicable only to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER SnapshotVM Base image snapshot for RDS Servers. .PARAMETER VmFolder VM folder to deploy the RDSServers to. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER HostOrCluster Host or cluster to deploy the RDSServers in. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER ResourcePool Resource pool to deploy the RDSServers. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER Datastores Datastore names to store the RDSServer. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER UseVSAN Whether to use vSphere VSAN. This is applicable for vSphere 5.5 or later. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER EnableProvisioning Set to true to enable provision of RDSServers immediately in farm. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER StopOnProvisioningError Set to true to stop provisioning of all RDSServers on error. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER TransparentPageSharingScope The transparent page sharing scope. @@ -1798,7 +1917,7 @@ function New-HVFarm { .PARAMETER NamingMethod Determines how the VMs in the farm are named. Set PATTERN to use naming pattern. - The default value is PATTERN. Curentlly only PATTERN is allowed. + The default value is PATTERN. Currently only PATTERN is allowed. .PARAMETER NamingPattern RDS Servers will be named according to the specified naming pattern. @@ -1813,26 +1932,42 @@ function New-HVFarm { .PARAMETER MaximumCount Maximum number of Servers in the farm. The default value is 1. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER AdContainer This is the Active Directory container which the Servers will be added to upon creation. The default value is 'CN=Computers'. - Applicable to Linked Clone farm. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER NetBiosName Domain Net Bios Name. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER DomainAdmin Domain Administrator user name which will be used to join the domain. Default value is null. - Applicable to Linked Clone farms. + Applicable to Linked Clone and Instant Clone farms. .PARAMETER SysPrepName The customization spec to use. Applicable to Linked Clone farms. +.PARAMETER PowerOffScriptName + Power off script. ClonePrep can run a customization script on instant-clone machines before they are powered off. Provide the path to the script on the parent virtual machine. + Applicable to Instant Clone farms. + +.PARAMETER PowerOffScriptParameters + Power off script parameters. Example: p1 p2 p3 + Applicable to Instant Clone farms. + +.PARAMETER PostSynchronizationScriptName + Post synchronization script. ClonePrep can run a customization script on instant-clone machines after they are created or recovered or a new image is pushed. Provide the path to the script on the parent virtual machine. + Applicable to Instant Clone farms. + +.PARAMETER PostSynchronizationScriptParameters + Post synchronization script parameters. Example: p1 p2 p3 + Applicable to Instant Clone farms. + .PARAMETER RdsServers List of existing registered RDS server names to add into manual farm. Applicable to Manual farms. @@ -1841,28 +1976,43 @@ function New-HVFarm { Path of the JSON specification file. .PARAMETER HvServer - Reference to Horizon View Server to query the farms from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. + Reference to Horizon View Server to query the farms from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE - New-HVFarm -LinkedClone -FarmName 'LCFarmTest' -ParentVM 'Win_Server_2012_R2' -SnapshotVM 'Snap_RDS' -VmFolder 'PoolVM' -HostOrCluster 'cls' -ResourcePool 'cls' -Datastores 'datastore1 (5)' -FarmDisplayName 'LC Farm Test' -Description  'created LC Farm from PS' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern  "LCFarmVM_PS" -MinReady 1 -MaximumCount 1 -SysPrepName "RDSH_Cust2" -NetBiosName "adviewdev" + New-HVFarm -LinkedClone -FarmName 'LCFarmTest' -ParentVM 'Win_Server_2012_R2' -SnapshotVM 'Snap_RDS' -VmFolder 'PoolVM' -HostOrCluster 'cls' -ResourcePool 'cls' -Datastores 'datastore1 (5)' -FarmDisplayName 'LC Farm Test' -Description 'created LC Farm from PS' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "LCFarmVM_PS" -MinReady 1 -MaximumCount 1 -SysPrepName "RDSH_Cust2" -NetBiosName "adviewdev" + Creates new linkedClone farm by using naming pattern .EXAMPLE - New-HVFarm -Spec C:\VMWare\Specs\LinkedClone.json + New-HVFarm -InstantClone -FarmName 'ICFarmCL' -ParentVM 'vm-rdsh-ic' -SnapshotVM 'Snap_5' -VmFolder 'Instant_Clone_VMs' -HostOrCluster 'vimal-cluster' -ResourcePool 'vimal-cluster' -Datastores 'datastore1' -FarmDisplayName 'IC Farm using CL' -Description 'created IC Farm from PS command-line' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "ICFarmCL-" -NetBiosName "ad-vimalg" + Creates new linkedClone farm by using naming pattern .EXAMPLE - New-HVFarm -Manual -FarmName "manualFarmTest" -FarmDisplayName "manualFarmTest" -Description "Manual PS Test" -RdsServers "vm-for-rds.eng.vmware.com","vm-for-rds-2.eng.vmware.com" + New-HVFarm -Spec C:\VMWare\Specs\LinkedClone.json -Confirm:$false + Creates new linkedClone farm by using json file + +.EXAMPLE + New-HVFarm -Spec C:\VMWare\Specs\InstantCloneFarm.json -Confirm:$false + Creates new instantClone farm by using json file + +.EXAMPLE + New-HVFarm -Manual -FarmName "manualFarmTest" -FarmDisplayName "manualFarmTest" -Description "Manual PS Test" -RdsServers "vm-for-rds.eng.vmware.com","vm-for-rds-2.eng.vmware.com" -Confirm:$false + Creates new manual farm by using rdsServers names + +.EXAMPLE + New-HVFarm -Spec C:\VMWare\Specs\AutomatedInstantCloneFarm.json -FarmName 'InsPool' -NamingPattern 'InsFarm-' + Creates new instant clone farm by reading few parameters from json and few parameters from command line. .OUTPUTS None .NOTES - Author : Ankit Gupta. - Author email : guptaa@vmware.com - Version : 1.0 + Author : praveen mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -1876,6 +2026,10 @@ function New-HVFarm { [switch] $LinkedClone, + [Parameter(Mandatory = $true,ParameterSetName = "INSTANT_CLONE")] + [switch] + $InstantClone, + [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [switch] $Manual, @@ -1883,6 +2037,8 @@ function New-HVFarm { #farmSpec.farmData.name [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = "INSTANT_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'JSON_FILE')] [string] $FarmName, @@ -1906,102 +2062,274 @@ function New-HVFarm { [boolean] $Enable = $true, - #farmSpec.automatedfarmSpec.virtualCenter if LINKED_CLONE + #farmSpec.data.settings.disconnectedSessionTimeoutPolicy + [Parameter(Mandatory = $false)] + [ValidateSet("IMMEDIATE","NEVER","AFTER")] + [string] + $DisconnectedSessionTimeoutPolicy = "NEVER", + + #farmSpec.data.settings.disconnectedSessionTimeoutMinutes + [Parameter(Mandatory = $false)] + [ValidateRange(1,[Int]::MaxValue)] + [int] + $DisconnectedSessionTimeoutMinutes, + + #farmSpec.data.settings.emptySessionTimeoutPolicy + [Parameter(Mandatory = $false)] + [ValidateSet("NEVER","AFTER")] + [string] + $EmptySessionTimeoutPolicy = "AFTER", + + #farmSpec.data.settings.emptySessionTimeoutMinutes + [Parameter(Mandatory = $false)] + [ValidateSet(1,[Int]::MaxValue)] + [int] + $EmptySessionTimeoutMinutes = 1, + + #farmSpec.data.settings.logoffAfterTimeout + [Parameter(Mandatory = $false)] + [boolean] + $LogoffAfterTimeout = $false, + + #farmSpec.data.displayProtocolSettings.defaultDisplayProtocol + [Parameter(Mandatory = $false)] + [ValidateSet("RDP","PCOIP","BLAST")] + [string] + $DefaultDisplayProtocol = "PCOIP", + + #farmSpec.data.displayProtocolSettings.allowDisplayProtocolOverride + [Parameter(Mandatory = $false)] + [boolean] + $AllowDisplayProtocolOverride = $true, + + #farmSpec.data.displayProtocolSettings.enableHTMLAccess + [Parameter(Mandatory = $false)] + [boolean] + $EnableHTMLAccess = $false, + + #farmSpec.data.serverErrorThreshold + [Parameter(Mandatory = $false)] + [ValidateRange(0,[Int]::MaxValue)] + $ServerErrorThreshold = 0, + + #farmSpec.data.mirageConfigurationOverrides.overrideGlobalSetting + [Parameter(Mandatory = $false)] + [boolean] + $OverrideGlobalSetting = $false, + + #farmSpec.data.mirageConfigurationOverrides.enabled + [Parameter(Mandatory = $false)] + [boolean] + $MirageServerEnabled, + + #farmSpec.data.mirageConfigurationOverrides.url + [Parameter(Mandatory = $false)] + [string] + $Url, + + #farmSpec.automatedfarmSpec.virtualCenter if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [string] $Vcenter, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.parentVM if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.parentVM if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string] $ParentVM, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.snapshotVM if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.snapshotVM if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string] $SnapshotVM, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.vmFolder if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.vmFolder if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string] $VmFolder, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.hostOrCluster if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.hostOrCluster if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string] $HostOrCluster, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.resourcePool if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.resourcePool if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string] $ResourcePool, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastore if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.dataCenter if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [string] + $dataCenter, + + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastore if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string[]] $Datastores, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.useVSAN if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastores.storageOvercommit if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] - [string] - $UseVSAN, + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [string[]] + $StorageOvercommit = $null, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.enableProvsioning if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.useVSAN if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [boolean] + $UseVSAN = $false, + + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.enableProvsioning if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [boolean] $EnableProvisioning = $true, - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.stopOnProvisioningError if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.stopOnProvisioningError if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [boolean] $StopOnProvisioningError = $true, [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [string] $TransparentPageSharingScope = 'VM', - #farmSpec.automatedfarmSpec.rdsServerNamingSpec.namingMethod if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE + #farmSpec.automatedfarmSpec.rdsServerNamingSpec.namingMethod if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [ValidateSet('PATTERN')] [string] $NamingMethod = 'PATTERN', - #farmSpec.automatedfarmSpec.rdsServerNamingSpec.patternNamingSettings.namingPattern if LINKED_CLONE + #farmSpec.automatedfarmSpec.rdsServerNamingSpec.patternNamingSettings.namingPattern if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'JSON_FILE')] [string] $NamingPattern = $farmName + '{n:fixed=4}', - #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [int] $MinReady = 0, - #farmSpec.automatedfarmSpec.rdsServerNamingSpec.patternNamingSettings.maxNumberOfRDSServers if LINKED_CLONE + #farmSpec.automatedfarmSpec.rdsServerNamingSpec.patternNamingSettings.maxNumberOfRDSServers if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [int] $MaximumCount = 1, - #farmSpec.automatedfarmSpec.customizationSettings.adContainer if LINKED_CLONE + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.useSeparateDatastoresReplicaAndOSDisks if INSTANT_CLONE, LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [boolean] + $UseSeparateDatastoresReplicaAndOSDisks = $false, + + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.replicaDiskDatastore, if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [string] + $ReplicaDiskDatastore, + + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.useNativeSnapshots, if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [boolean] + $UseNativeSnapshots = $false, + + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.reclaimVmDiskSpace, if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [boolean] + $ReclaimVmDiskSpace = $false, + + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.reclamationThresholdGB + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(0,[Int]::MaxValue)] + [int] + $ReclamationThresholdGB = 1, + + #farmSpec.automatedfarmSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.blackoutTimes + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [VMware.Hv.FarmBlackoutTime[]] + $BlackoutTimes, + + #farmSpec.automatedfarmSpec.customizationSettings.adContainer if LINKED_CLONE, INSTANT_CLONE [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "INSTANT_CLONE")] [string] $AdContainer = 'CN=Computers', #farmSpec.automatedfarmSpec.customizationSettings.domainAdministrator + #farmSpec.automatedfarmSpec.customizationSettings.cloneprepCustomizationSettings.instantCloneEngineDomainAdministrator [Parameter(Mandatory = $true,ParameterSetName = 'LINKED_CLONE')] + [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] [string] $NetBiosName, #farmSpec.automatedfarmSpec.customizationSettings.domainAdministrator + #farmSpec.automatedfarmSpec.customizationSettings.cloneprepCustomizationSettings.instantCloneEngineDomainAdministrator [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "INSTANT_CLONE")] [string] $DomainAdmin = $null, + #farmSpec.automatedfarmSpec.customizationSettings.reusePreExistingAccounts + [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [Boolean] + $ReusePreExistingAccounts = $false, + #farmSpec.automatedfarmSpec.customizationSettings.sysprepCustomizationSettings.customizationSpec if LINKED_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] [string] $SysPrepName, - ##farmSpec.manualfarmSpec.rdsServers + #desktopSpec.automatedfarmSpec.customizationSettings.cloneprepCustomizationSettings.powerOffScriptName if INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [string] + $PowerOffScriptName, + + #farmSpec.automatedfarmSpec.customizationSettings.cloneprepCustomizationSettings.powerOffScriptParameters if INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [string] + $PowerOffScriptParameters, + + #farmSpec.automatedfarmSpec.customizationSettings.cloneprepCustomizationSettings.postSynchronizationScriptName if INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [string] + $PostSynchronizationScriptName, + + #farmSpec.automatedfarmSpec.customizationSettings.cloneprepCustomizationSettings.postSynchronizationScriptParameters if INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [string] + $PostSynchronizationScriptParameters, + + #farmSpec.automatedfarmSpec.rdsServerMaxSessionsData.maxSessionsType if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = "INSTANT_CLONE")] + [ValidateSet("UNLIMITED", "LIMITED")] + [string] + $MaxSessionsType = "UNLIMITED", + + #farmSpec.automatedfarmSpec.rdsServerMaxSessionsData.maxSessionsType if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = "INSTANT_CLONE")] + [ValidateRange(1, [int]::MaxValue)] + [int] + $MaxSessions, + + #farmSpec.manualfarmSpec.rdsServers [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [string[]] $RdsServers, @@ -2033,6 +2361,9 @@ function New-HVFarm { # ADContainerId # FarmSysprepCustomizationSettings # CustomizationSpecId + # FarmCloneprepCustomizationSettings + # InstantCloneEngineDomainAdministratorId + # # FarmManualfarmSpec # RDSServerId[] # @@ -2046,12 +2377,12 @@ function New-HVFarm { } process { - + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys if ($farmName) { try { - $sourceFarm = Get-HVFarm -farmName $farmName -hvServer $hvServer + $sourceFarm = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer -suppressInfo $true } catch { - Write-Error "Make sure Get-HVFarm advanced function is loaded, $_" + Write-Error "Make sure Get-HVFarmSummary advanced function is loaded, $_" break } if ($sourceFarm) { @@ -2067,18 +2398,51 @@ function New-HVFarm { Write-Error "Json file exception, $_" break } + try { + Test-HVFarmSpec -PoolObject $jsonObject + } catch { + Write-Error "Json object validation failed, $_" + break + } if ($jsonObject.type -eq 'AUTOMATED') { $farmType = 'AUTOMATED' + $provisioningType = $jsonObject.ProvisioningType if ($null -ne $jsonObject.AutomatedFarmSpec.VirtualCenter) { $vCenter = $jsonObject.AutomatedFarmSpec.VirtualCenter } - $linkedClone = $true + $netBiosName = $jsonObject.NetBiosName - $adContainer = $jsonObject.AutomatedFarmSpec.CustomizationSettings.AdContainer + if (!$jsonObject.AutomatedFarmSpec.CustomizationSettings.AdContainer) { + Write-Host "adContainer was empty using CN=Computers" + } else { + $AdContainer = $jsonObject.AutomatedFarmSpec.CustomizationSettings.AdContainer + } + + #populate customization settings attributes based on the cutomizationType + if ($jsonObject.AutomatedFarmSpec.ProvisioningType -eq "INSTANT_CLONE_ENGINE") { + $InstantClone = $true + if ($null -ne $jsonObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings) { + $DomainAdmin = $jsonObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.InstantCloneEngineDomainAdministrator + $powerOffScriptName = $jsonObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.PowerOffScriptName + $powerOffScriptParameters = $jsonObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.PowerOffScriptParameters + $postSynchronizationScriptName = $jsonObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.PostSynchronizationScriptName + $postSynchronizationScriptParameters = $jsonObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.PostSynchronizationScriptParameters + } + } elseif ($jsonObject.AutomatedFarmSpec.ProvisioningType -eq "VIEW_COMPOSER") { + $LinkedClone = $true + $DomainAdmin = $jsonObject.AutomatedFarmSpec.CustomizationSettings.domainAdministrator + $reusePreExistingAccounts = $jsonObject.AutomatedFarmSpec.CustomizationSettings.ReusePreExistingAccounts + $sysPrepName = $jsonObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings.CustomizationSpec + } $namingMethod = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.NamingMethod - $namingPattern = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.namingPattern + if ($NamingPattern -eq '{n:fixed=4}') { + $namingPattern = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.namingPattern + } $maximumCount = $jsonObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.maxNumberOfRDSServers + $enableProvisioning = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.EnableProvisioning + $stopProvisioningOnError = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.StopProvisioningOnError + $minReady = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.MinReadyVMsOnVComposerMaintenance $transparentPageSharingScope = $jsonObject.AutomatedFarmSpec.virtualCenterManagedCommonSettings.TransparentPageSharingScope @@ -2092,10 +2456,44 @@ function New-HVFarm { $hostOrCluster = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.HostOrCluster $resourcePool = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.ResourcePool $dataStoreList = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.Datastores + foreach ($dtStore in $dataStoreList) { $datastores += $dtStore.Datastore + $storageOvercommit += $dtStore.StorageOvercommit } - $sysPrepName = $jsonObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings.CustomizationSpec + $useVSan = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.UseVSan + + ## ViewComposerStorageSettings for Linked-Clone farms + if ($LinkedClone -or $InstantClone) { + $useSeparateDatastoresReplicaAndOSDisks = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.UseSeparateDatastoresReplicaAndOSDisks + if ($useSeparateDatastoresReplicaAndOSDisks) { + $replicaDiskDatastore = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.ReplicaDiskDatastore + } + if ($LinkedClone) { + #For Instant clone desktops, this setting can only be set to false + $useNativeSnapshots = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.UseNativeSnapshots + $reclaimVmDiskSpace = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.ReclaimVmDiskSpace + if ($reclaimVmDiskSpace) { + $ReclamationThresholdGB = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.ReclamationThresholdGB + if ($null -ne $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.blackoutTimes) { + $blackoutTimesList = $jsonObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.ViewComposerStorageSettings.SpaceReclamationSettings.blackoutTimes + foreach ($blackout in $blackoutTimesList) { + $blackoutObj = New-Object VMware.Hv.DesktopBlackoutTime + $blackoutObj.Days = $blackout.Days + $blackoutObj.StartTime = $blackout.StartTime + $blackoutObj.EndTime = $blackoutObj.EndTime + $blackoutTimes += $blackoutObj + } + } + } + } + } + + $maxSessionsType = $jsonObject.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType + if ($maxSessionsType -eq "LIMITED") { + $maxSessions = $jsonObject.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessions + } + } elseif ($jsonObject.type -eq 'MANUAL') { $manual = $true $farmType = 'MANUAL' @@ -2105,16 +2503,44 @@ function New-HVFarm { $rdsServers += $RdsServerObj.rdsServer } } - $farmDisplayName = $jsonObject.data.DisplayName - $description = $jsonObject.data.Description - $accessGroup = $jsonObject.data.AccessGroup - $farmName = $jsonObject.data.name + $farmDisplayName = $jsonObject.Data.DisplayName + $description = $jsonObject.Data.Description + $accessGroup = $jsonObject.Data.AccessGroup + if (! $FarmName) { + $farmName = $jsonObject.Data.name + } + if ($null -ne $jsonObject.Data.Enabled) { + $enable = $jsonObject.Data.Enabled + } + if ($null -ne $jsonObject.Data.Settings) { + $disconnectedSessionTimeoutPolicy = $jsonObject.Data.Settings.DisconnectedSessionTimeoutPolicy + $disconnectedSessionTimeoutMinutes = $jsonObject.Data.Settings.DisconnectedSessionTimeoutMinutes + $emptySessionTimeoutPolicy = $jsonObject.Data.Settings.EmptySessionTimeoutPolicy + $emptySessionTimeoutMinutes = $jsonObject.Data.Settings.EmptySessionTimeoutMinutes + $logoffAfterTimeout = $jsonObject.Data.Settings.LogoffAfterTimeout + } + if ($null -ne $jsonObject.Data.DisplayProtocolSettings) { + $defaultDisplayProtocol = $jsonObject.Data.DisplayProtocolSettings.DefaultDisplayProtocol + $allowDisplayProtocolOverride = $jsonObject.Data.DisplayProtocolSettings.AllowDisplayProtocolOverride + $enableHTMLAccess = $jsonObject.Data.DisplayProtocolSettings.EnableHTMLAccess + } + if ($null -ne $jsonObject.Data.serverErrorThreshold) { + $serverErrorThreshold = $jsonObject.Data.serverErrorThreshold + } + if ($null -ne $jsonObject.Data.MirageConfigurationOverrides) { + $overrideGlobalSetting = $jsonObject.Data.MirageConfigurationOverrides.OverrideGlobalSetting + $mirageserverEnabled = $jsonObject.Data.MirageConfigurationOverrides.Enabled + $url = $jsonObject.Data.MirageConfigurationOverrides.url + } } if ($linkedClone) { $farmType = 'AUTOMATED' $provisioningType = 'VIEW_COMPOSER' - } elseif ($manual) { + } elseif ($InstantClone) { + $farmType = 'AUTOMATED' + $provisioningType = 'INSTANT_CLONE_ENGINE' + }elseif ($manual) { $farmType = 'MANUAL' } @@ -2167,17 +2593,17 @@ function New-HVFarm { $farmVirtualCenterManagedCommonSettings = $farmSpecObj.AutomatedFarmSpec.virtualCenterManagedCommonSettings } - if (!$farmVirtualMachineNamingSpec) { + if ($farmSpecObj.AutomatedFarmSpec.RdsServerNamingSpec) { $farmSpecObj.AutomatedFarmSpec.RdsServerNamingSpec.NamingMethod = $namingMethod $farmSpecObj.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.namingPattern = $namingPattern $farmSpecObj.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.maxNumberOfRDSServers = $maximumCount } else { $vmNamingSpec = New-Object VMware.Hv.FarmRDSServerNamingSpec - $vmNamingSpec.NamingMethod = 'PATTERN' - + $vmNamingSpec.NamingMethod = $namingMethod $vmNamingSpec.patternNamingSettings = New-Object VMware.Hv.FarmPatternNamingSettings $vmNamingSpec.patternNamingSettings.namingPattern = $namingPattern $vmNamingSpec.patternNamingSettings.maxNumberOfRDSServers = $maximumCount + $farmSpecObj.AutomatedFarmSpec.RdsServerNamingSpec = $vmNamingSpec } # @@ -2185,8 +2611,10 @@ function New-HVFarm { # try { $farmVirtualCenterProvisioningData = Get-HVFarmProvisioningData -vc $virtualCenterID -vmObject $farmVirtualCenterProvisioningData - $hostClusterId = $farmVirtualCenterProvisioningData.HostOrCluster - $farmVirtualCenterStorageSettings = Get-HVFarmStorageObject -hostclusterID $hostClusterId -storageObject $farmVirtualCenterStorageSettings + + $HostOrCluster_helper = New-Object VMware.Hv.HostOrClusterService + $hostClusterIds = (($HostOrCluster_helper.HostOrCluster_GetHostOrClusterTree($services, $farmVirtualCenterProvisioningData.datacenter)).treeContainer.children.info).Id + $farmVirtualCenterStorageSettings = Get-HVFarmStorageObject -hostclusterIDs $hostClusterIds -storageObject $farmVirtualCenterStorageSettings $farmVirtualCenterNetworkingSettings = Get-HVFarmNetworkSetting -networkObject $farmVirtualCenterNetworkingSettings $farmCustomizationSettings = Get-HVFarmCustomizationSetting -vc $virtualCenterID -customObject $farmCustomizationSettings } catch { @@ -2195,31 +2623,20 @@ function New-HVFarm { break } - $farmSpecObj.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType = "UNLIMITED" - - if (!$FarmVirtualCenterProvisioningSettings) { - $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.enableProvisioning = $true - $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.stopProvisioningOnError = $true - $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance = 0 - $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData = $farmVirtualCenterProvisioningData - $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings = $farmVirtualCenterStorageSettings - $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterNetworkingSettings = $FarmVirtualCenterNetworkingSettings - - $farmSpecObj.AutomatedFarmSpec.CustomizationSettings = $farmCustomizationSettings - $farmSpecObj.AutomatedFarmSpec.ProvisioningType = $provisioningType - $farmSpecObj.AutomatedFarmSpec.VirtualCenter = $virtualCenterID - } else { - $FarmVirtualCenterProvisioningSettings.VirtualCenterProvisioningData = $farmVirtualCenterProvisioningData - $FarmVirtualCenterProvisioningSettings.VirtualCenterStorageSettings = $farmVirtualCenterStorageSettings - $FarmVirtualCenterProvisioningSettings.VirtualCenterNetworkingSettings = $FarmVirtualCenterNetworkingSettings - - $FarmAutomatedFarmSpec = New-Object VMware.Hv.FarmAutomatedFarmSpec - $FarmAutomatedFarmSpec.ProvisioningType = $provisioningType - $FarmAutomatedFarmSpec.VirtualCenter = $virtualCenterID - $FarmAutomatedFarmSpec.VirtualCenterProvisioningSettings = $farmVirtualCenterProvisioningSettings - $FarmAutomatedFarmSpec.virtualCenterManagedCommonSettings = $farmVirtualCenterManagedCommonSettings - $FarmAutomatedFarmSpec.CustomizationSettings = $farmCustomizationSettings + $farmSpecObj.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType = $maxSessionsType + if ($maxSessionsType -eq "LIMITED") { + $farmSpecObj.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType = $maxSessions } + $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.enableProvisioning = $enableProvisioning + $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.stopProvisioningOnError = $stopProvisioningOnError + $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance = $minReady + $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData = $farmVirtualCenterProvisioningData + $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings = $farmVirtualCenterStorageSettings + $farmSpecObj.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterNetworkingSettings = $FarmVirtualCenterNetworkingSettings + + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings = $farmCustomizationSettings + $farmSpecObj.AutomatedFarmSpec.ProvisioningType = $provisioningType + $farmSpecObj.AutomatedFarmSpec.VirtualCenter = $virtualCenterID } } @@ -2229,12 +2646,34 @@ function New-HVFarm { $farmData = $farmSpecObj.data $AccessGroup_service_helper = New-Object VMware.Hv.AccessGroupService - $ag = $AccessGroup_service_helper.AccessGroup_List($services) | Where-Object { $_.base.name -eq $accessGroup } - $farmData.AccessGroup = $ag.id + $farmData.AccessGroup = Get-HVAccessGroupID $AccessGroup_service_helper.AccessGroup_List($services) $farmData.name = $farmName $farmData.DisplayName = $farmDisplayName $farmData.Description = $description + if ($farmData.Settings) { + $farmData.Settings.DisconnectedSessionTimeoutPolicy = $disconnectedSessionTimeoutPolicy + if ($disconnectedSessionTimeoutPolicy -eq "AFTER") { + $farmData.Settings.DisconnectedSessionTimeoutMinutes = $disconnectedSessionTimeoutMinutes + } + $farmData.Settings.EmptySessionTimeoutPolicy = $emptySessionTimeoutPolicy + if ($emptySessionTimeoutPolicy -eq "AFTER") { + $farmData.Settings.EmptySessionTimeoutMinutes = $emptySessionTimeoutMinutes + } + $logoffAfterTimeout = $farmData.Settings.logoffAfterTimeout + } + if ($farmData.DisplayProtocolSettings) { + $farmData.DisplayProtocolSettings.DefaultDisplayProtocol = $defaultDisplayProtocol + $farmData.DisplayProtocolSettings.AllowDisplayProtocolOverride = $AllowDisplayProtocolOverride + $farmData.DisplayProtocolSettings.EnableHTMLAccess = $enableHTMLAccess + } + if ($farmData.MirageConfigurationOverrides){ + $farmData.MirageConfigurationOverrides.OverrideGlobalSetting = $overrideGlobalSetting + $farmData.MirageConfigurationOverrides.Enabled = $mirageServerEnabled + if ($url) { + $farmData.MirageConfigurationOverrides.Url = $url + } + } $farmSpecObj.type = $farmType if ($FarmAutomatedFarmSpec) { @@ -2246,10 +2685,21 @@ function New-HVFarm { # Please uncomment below code, if you want to save the json file <# -$myDebug = convertto-json -InputObject $farmSpecObj -depth 12 -$myDebug | out-file -filepath c:\temp\copiedfarm.json -#> - $farm_service_helper.Farm_Create($services, $farmSpecObj) + $myDebug = convertto-json -InputObject $farmSpecObj -depth 12 + $myDebug | out-file -filepath c:\temp\copiedfarm.json + #> + + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($farmSpecObj.data.name)) { + $Id = $farm_service_helper.Farm_Create($services, $farmSpecObj) + } else { + try { + Test-HVFarmSpec -PoolObject $farmSpecObj + } catch { + Write-Error "FarmSpec object validation failed, $_" + break + } + } + return $farmSpecObj } end { @@ -2258,6 +2708,89 @@ $myDebug | out-file -filepath c:\temp\copiedfarm.json } +function Test-HVFarmSpec { + param( + [Parameter(Mandatory = $true)] + $PoolObject + ) + if ($null -eq $PoolObject.Type) { + Throw "Specify type of farm" + } + $jsonFarmTypeArray = @('AUTOMATED','MANUAL') + if (! ($jsonFarmTypeArray -contains $PoolObject.Type)) { + Throw "Farm type must be AUTOMATED or MANUAL" + } + if ($null -eq $PoolObject.Data.Name) { + Throw "Specify farm name" + } + if ($null -eq $PoolObject.Data.AccessGroup) { + Throw "Specify horizon access group" + } + if ($PoolObject.Type -eq "AUTOMATED"){ + $jsonProvisioningType = $PoolObject.AutomatedFarmSpec.ProvisioningType + if ($null -eq $jsonProvisioningType) { + Throw "Must specify provisioningType" + } + if ($null -eq $PoolObject.AutomatedFarmSpec.RdsServerNamingSpec.namingMethod) { + Throw "Must specify naming method to PATTERN" + } + if ($null -eq $PoolObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings) { + Throw "Specify Naming pattern settings" + } + if ($null -eq $PoolObject.AutomatedFarmSpec.RdsServerNamingSpec.patternNamingSettings.namingPattern) { + Throw "Specify specified naming pattern" + } + if ($null -eq $PoolObject.AutomatedFarmSpec.virtualCenterProvisioningSettings.enableProvisioning) { + Throw "Specify Whether to enable provisioning or not" + } + if ($null -eq $PoolObject.AutomatedFarmSpec.virtualCenterProvisioningSettings.stopProvisioningOnError) { + Throw "Specify Whether provisioning on all VMs stops on error" + } + $jsonTemplate = $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Template + $jsonParentVm = $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ParentVm + $jsonSnapshot = $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Snapshot + $jsonVmFolder = $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.VmFolder + $jsonHostOrCluster = $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.HostOrCluster + $ResourcePool = $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ResourcePool + if (!( ($null -ne $jsonTemplate) -or (($null -ne $jsonParentVm) -and ($null -ne $jsonSnapshot) )) ) { + Throw "Must specify Template or (ParentVm and Snapshot) names" + } + if ($null -eq $jsonVmFolder) { + Throw "Must specify VM folder to deploy the VMs" + } + if ($null -eq $jsonHostOrCluster) { + Throw "Must specify Host or cluster to deploy the VMs" + } + if ($null -eq $resourcePool) { + Throw "Must specify Resource pool to deploy the VMs" + } + if ($null -eq $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.Datastores) { + Throw "Must specify datastores names" + } + if ($null -eq $PoolObject.AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.useVSan) { + Throw "Must specify whether to use virtual SAN or not" + } + $customizationType = $PoolObject.AutomatedFarmSpec.CustomizationSettings.customizationType + if ($null -eq $customizationType) { + Throw "Specify customization type" + } + if ($customizationType -eq 'SYS_PREP' -and $null -eq $PoolObject.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings) { + Throw "Specify sysPrep customization settings" + } + if ($customizationType -eq 'CLONE_PREP' -and $null -eq $PoolObject.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings) { + Throw "Specify clone customization settings" + } + if ($null -eq $PoolObject.AutomatedFarmSpec.RdsServerMaxSessionsData.MaxSessionsType) { + Throw "Specify MaxSessionsType" + } + } elseif ($PoolObject.Type -eq "MANUAL") { + if ($null -eq $PoolObject.manualFarmSpec.rdsServers) { + Throw "Specify rdsServers name" + } + } +} + + function Get-HVFarmProvisioningData { param( [Parameter(Mandatory = $false)] @@ -2278,6 +2811,12 @@ function Get-HVFarmProvisioningData { } $vmObject.ParentVm = $parentVMObj.id $dataCenterID = $parentVMObj.datacenter + if ($dataCenter -and $dataCenterID) { + $baseImageVmInfo = $base_imageVm_helper.BaseImageVm_ListByDatacenter($dataCenterID) + if (! ($baseImageVmInfo.Path -like "/$dataCenter/*")) { + throw "$parentVM not exists in datacenter: [$dataCenter]" + } + } $vmObject.datacenter = $dataCenterID } if ($snapshotVM) { @@ -2311,42 +2850,45 @@ function Get-HVFarmProvisioningData { } if ($hostOrCluster) { $HostOrCluster_service_helper = New-Object VMware.Hv.HostOrClusterService - $hostClusterList = ($HostOrCluster_service_helper.HostOrCluster_GetHostOrClusterTree($services, $vmobject.datacenter)).treeContainer.children.info - $HostClusterObj = $hostClusterList | Where-Object { $_.name -eq $hostOrCluster } - if ($null -eq $HostClusterObj) { - throw "No host or cluster found with name: [$hostOrCluster]" + $vmObject.HostOrCluster = Get-HVHostOrClusterID $HostOrCluster_service_helper.HostOrCluster_GetHostOrClusterTree($services,$vmobject.datacenter) + if ($null -eq $vmObject.HostOrCluster) { + throw "No hostOrCluster found with Name: [$hostOrCluster]" } - $vmObject.HostOrCluster = $HostClusterObj.id } if ($resourcePool) { $ResourcePool_service_helper = New-Object VMware.Hv.ResourcePoolService - $resourcePoolList = $ResourcePool_service_helper.ResourcePool_GetResourcePoolTree($services, $vmobject.HostOrCluster) - $resourcePoolObj = $resourcePoolList | Where-Object { $_.resourcepooldata.name -eq $resourcePool } - if ($null -eq $resourcePoolObj) { - throw "No resource pool found with name: [$resourcePool]" + $vmObject.ResourcePool = Get-HVResourcePoolID $ResourcePool_service_helper.ResourcePool_GetResourcePoolTree($services,$vmobject.HostOrCluster) + if ($null -eq $vmObject.ResourcePool) { + throw "No Resource Pool found with Name: [$resourcePool]" } - $vmObject.ResourcePool = $resourcePoolObj.id } return $vmObject } + function Get-HVFarmStorageObject { param( - [Parameter(Mandatory = $false)] - [VMware.Hv.FarmVirtualCenterStorageSettings]$StorageObject, [Parameter(Mandatory = $true)] - [VMware.Hv.HostOrClusterId]$HostClusterID + [VMware.Hv.HostOrClusterId[]]$HostClusterIDs, + + [Parameter(Mandatory = $false)] + [VMware.Hv.FarmVirtualCenterStorageSettings]$StorageObject ) if (!$storageObject) { $storageObject = New-Object VMware.Hv.FarmVirtualCenterStorageSettings $FarmSpaceReclamationSettings = New-Object VMware.Hv.FarmSpaceReclamationSettings -Property @{ 'reclaimVmDiskSpace' = $false } + if ($reclaimVmDiskSpace) { + $FarmSpaceReclamationSettings.ReclamationThresholdGB = $reclamationThresholdGB + if ($blackoutTimes) { + $FarmSpaceReclamationSettings.BlackoutTimes = $blackoutTimes + } + } $FarmViewComposerStorageSettingsList = @{ - 'useSeparateDatastoresReplicaAndOSDisks' = $false; - 'replicaDiskDatastore' = $FarmReplicaDiskDatastore - 'useNativeSnapshots' = $false; + 'useSeparateDatastoresReplicaAndOSDisks' = $UseSeparateDatastoresReplicaAndOSDisks; + 'useNativeSnapshots' = $useNativeSnapshots; 'spaceReclamationSettings' = $FarmSpaceReclamationSettings; } @@ -2354,18 +2896,35 @@ function Get-HVFarmStorageObject { } if ($datastores) { + if ($StorageOvercommit -and ($datastores.Length -ne $StorageOvercommit.Length) ) { + throw "Parameters datastores length: [$datastores.Length] and StorageOvercommit length: [$StorageOvercommit.Length] should be of same size" + } $Datastore_service_helper = New-Object VMware.Hv.DatastoreService - $datastoreList = $Datastore_service_helper.Datastore_ListDatastoresByHostOrCluster($services, $hostClusterID) + foreach ($hostClusterID in $hostClusterIDs) { + $datastoreList += $Datastore_service_helper.Datastore_ListDatastoresByHostOrCluster($services, $hostClusterID) + } $datastoresSelected = @() foreach ($ds in $datastores) { $datastoresSelected += ($datastoreList | Where-Object { $_.datastoredata.name -eq $ds }).id } + if (! $storageOvercommit) { + foreach ($ds in $datastoresSelected) { + $storageOvercommit += ,'UNBOUNDED' + } + } + $StorageOvercommitCnt = 0 foreach ($ds in $datastoresSelected) { $datastoresObj = New-Object VMware.Hv.FarmVirtualCenterDatastoreSettings $datastoresObj.Datastore = $ds - $datastoresObj.StorageOvercommit = 'UNBOUNDED' + $datastoresObj.StorageOvercommit = $storageOvercommit[$StorageOvercommitCnt] $StorageObject.Datastores += $datastoresObj } + if ($useSeparateDatastoresReplicaAndOSDisks) { + $StorageObject.ViewComposerStorageSettings.UseSeparateDatastoresReplicaAndOSDisks = $UseSeparateDatastoresReplicaAndOSDisks + $FarmReplicaDiskDatastore = ($datastoreList | Where-Object { $_.datastoredata.name -eq $replicaDiskDatastore }).id + $StorageObject.ViewComposerStorageSettings.ReplicaDiskDatastore = $FarmReplicaDiskDatastore + } + } if ($storageObject.Datastores.Count -eq 0) { throw "No datastores found with name: [$datastores]" @@ -2374,6 +2933,7 @@ function Get-HVFarmStorageObject { return $storageObject } + function Get-HVFarmNetworkSetting { param( [Parameter(Mandatory = $false)] @@ -2385,6 +2945,7 @@ function Get-HVFarmNetworkSetting { return $networkObject } + function Get-HVFarmCustomizationSetting { param( [Parameter(Mandatory = $false)] @@ -2394,44 +2955,98 @@ function Get-HVFarmCustomizationSetting { [VMware.Hv.VirtualCenterId]$VcID ) if (!$customObject) { - $ViewComposerDomainAdministrator_service_helper = New-Object VMware.Hv.ViewComposerDomainAdministratorService - $ViewComposerDomainAdministratorID = ($ViewComposerDomainAdministrator_service_helper.ViewComposerDomainAdministrator_List($services, $vcID) | Where-Object { $_.base.domain -match $netBiosName }) - if (! [string]::IsNullOrWhitespace($domainAdmin)) { - $ViewComposerDomainAdministratorID = ($ViewComposerDomainAdministratorID | Where-Object { $_.base.userName -ieq $domainAdmin }).id - } else { - $ViewComposerDomainAdministratorID = $ViewComposerDomainAdministratorID[0].id + # View Composer and Instant Clone Engine Active Directory container for QuickPrep and ClonePrep. This must be set for Instant Clone Engine or SVI sourced desktops. + if ($InstantClone -or $LinkedClone) { + $ad_domain_helper = New-Object VMware.Hv.ADDomainService + $ADDomains = $ad_domain_helper.ADDomain_List($services) + if ($netBiosName) { + $adDomianId = ($ADDomains | Where-Object { $_.NetBiosName -eq $netBiosName } | Select-Object -Property id) + if ($null -eq $adDomianId) { + throw "No Domain found with netBiosName: [$netBiosName]" + } + } else { + $adDomianId = ($ADDomains[0] | Select-Object -Property id) + if ($null -eq $adDomianId) { + throw "No Domain configured in view administrator UI" + } + } + $ad_container_helper = New-Object VMware.Hv.AdContainerService + $adContainerId = ($ad_container_helper.ADContainer_ListByDomain($services,$adDomianId.id) | Where-Object { $_.Rdn -eq $adContainer } | Select-Object -Property id).id + if ($null -eq $adContainerId) { + throw "No AdContainer found with name: [$adContainer]" + } + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.AdContainer = $adContainerId } - if ($null -eq $ViewComposerDomainAdministratorID) { - throw "No Composer Domain Administrator found with netBiosName: [$netBiosName]" - } - $ADDomain_service_helper = New-Object VMware.Hv.ADDomainService - $adDomianId = ($ADDomain_service_helper.ADDomain_List($services) | Where-Object { $_.NetBiosName -eq $netBiosName } | Select-Object -Property id) - if ($null -eq $adDomianId) { - throw "No Domain found with netBiosName: [$netBiosName]" - } - $ad_containder_service_helper = New-Object VMware.Hv.AdContainerService - $adContainerId = ($ad_containder_service_helper.ADContainer_ListByDomain($services, $adDomianId.id) | Where-Object { $_.Rdn -eq $adContainer } | Select-Object -Property id).id - if ($null -eq $adContainerId) { - throw "No AdContainer found with name: [$adContainer]" - } - #Support only Sysprep Customization - $sysprepCustomizationSettings = $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings - # Get SysPrep CustomizationSpec ID - $CustomizationSpec_service_helper = New-Object VMware.Hv.CustomizationSpecService - $sysPrepIds = $CustomizationSpec_service_helper.CustomizationSpec_List($services, $vcID) | Where-Object { $_.customizationSpecData.name -eq $sysPrepName } | Select-Object -Property id - if ($sysPrepIds.Count -eq 0) { - throw "No Sysprep Customization spec found with Name: [$sysPrepName]" + if ($InstantClone) { + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CustomizationType = 'CLONE_PREP' + $instantCloneEngineDomainAdministrator_helper = New-Object VMware.Hv.InstantCloneEngineDomainAdministratorService + $insDomainAdministrators = $instantCloneEngineDomainAdministrator_helper.InstantCloneEngineDomainAdministrator_List($services) + $strFilterSet = @() + if (![string]::IsNullOrWhitespace($netBiosName)) { + $strFilterSet += '$_.namesData.dnsName -match $netBiosName' + } + if (![string]::IsNullOrWhitespace($domainAdmin)) { + $strFilterSet += '$_.base.userName -eq $domainAdmin' + } + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $instantCloneEngineDomainAdministrator = $insDomainAdministrators | Where $scriptBlock + If ($null -ne $instantCloneEngineDomainAdministrator) { + $instantCloneEngineDomainAdministrator = $instantCloneEngineDomainAdministrator[0].id + } elseif ($null -ne $insDomainAdministrators) { + $instantCloneEngineDomainAdministrator = $insDomainAdministrators[0].id + } + if ($null -eq $instantCloneEngineDomainAdministrator) { + throw "No Instant Clone Engine Domain Administrator found with netBiosName: [$netBiosName]" + } + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings = New-Object VMware.Hv.FarmClonePrepCustomizationSettings + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.InstantCloneEngineDomainAdministrator = $instantCloneEngineDomainAdministrator + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.powerOffScriptName = $powerOffScriptName + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.powerOffScriptParameters = $powerOffScriptParameters + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.postSynchronizationScriptName = $postSynchronizationScriptName + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CloneprepCustomizationSettings.postSynchronizationScriptParameters = $postSynchronizationScriptParameters + $customObject = $farmSpecObj.AutomatedFarmSpec.CustomizationSettings + } elseif ($LinkedClone) { + $ViewComposerDomainAdministrator_service_helper = New-Object VMware.Hv.ViewComposerDomainAdministratorService + $lcDomainAdministrators = $ViewComposerDomainAdministrator_service_helper.ViewComposerDomainAdministrator_List($services, $vcID) + $strFilterSet = @() + if (![string]::IsNullOrWhitespace($netBiosName)) { + $strFilterSet += '$_.base.domain -match $netBiosName' + } + if (![string]::IsNullOrWhitespace($domainAdmin)) { + $strFilterSet += '$_.base.userName -ieq $domainAdmin' + } + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $ViewComposerDomainAdministratorID = $lcDomainAdministrators | Where $scriptBlock + if ($null -ne $ViewComposerDomainAdministratorID) { + $ViewComposerDomainAdministratorID = $ViewComposerDomainAdministratorID[0].id + } elseif ($null -ne $lcDomainAdministrators) { + $ViewComposerDomainAdministratorID = $lcDomainAdministrators[0].id + } + if ($null -eq $ViewComposerDomainAdministratorID) { + throw "No Composer Domain Administrator found with netBiosName: [$netBiosName]" + } + + #Support only Sysprep Customization + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings = New-Object VMware.Hv.FarmSysprepCustomizationSettings + $sysprepCustomizationSettings = $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings + + # Get SysPrep CustomizationSpec ID + $CustomizationSpec_service_helper = New-Object VMware.Hv.CustomizationSpecService + $sysPrepIds = $CustomizationSpec_service_helper.CustomizationSpec_List($services, $vcID) | Where-Object { $_.customizationSpecData.name -eq $sysPrepName } | Select-Object -Property id + if ($sysPrepIds.Count -eq 0) { + throw "No Sysprep Customization spec found with Name: [$sysPrepName]" + } + $sysprepCustomizationSettings.CustomizationSpec = $sysPrepIds[0].id + + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CustomizationType = 'SYS_PREP' + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.DomainAdministrator = $ViewComposerDomainAdministratorID + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.ReusePreExistingAccounts = $reusePreExistingAccounts + $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings = $sysprepCustomizationSettings + $customObject = $farmSpecObj.AutomatedFarmSpec.CustomizationSettings } - $sysprepCustomizationSettings.CustomizationSpec = $sysPrepIds[0].id - - $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.CustomizationType = 'SYS_PREP' - $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.DomainAdministrator = $ViewComposerDomainAdministratorID - $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.AdContainer = $adContainerId - $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.ReusePreExistingAccounts = $false - $farmSpecObj.AutomatedFarmSpec.CustomizationSettings.SysprepCustomizationSettings = $sysprepCustomizationSettings - - $customObject = $farmSpecObj.AutomatedFarmSpec.CustomizationSettings } return $customObject } @@ -2454,12 +3069,16 @@ function Get-FarmSpec { if ($farmType -eq 'AUTOMATED') { $farm_spec_helper.getDataObject().AutomatedFarmSpec.RdsServerNamingSpec.PatternNamingSettings = $farm_helper.getFarmPatternNamingSettingsHelper().getDataObject() $farm_spec_helper.getDataObject().AutomatedFarmSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings = $farm_helper.getFarmViewComposerStorageSettingsHelper().getDataObject() - } elseif ($farmType -eq 'MANUAL') { - # No need to set + + } + $farm_spec_helper.getDataObject().Data.Settings = $farm_helper.getFarmSessionSettingsHelper().getDataObject() + $farm_spec_helper.getDataObject().Data.DisplayProtocolSettings = $farm_helper.getFarmDisplayProtocolSettingsHelper().getDataObject() + $farm_spec_helper.getDataObject().Data.MirageConfigurationOverrides = $farm_helper.getFarmMirageConfigurationOverridesHelper( ).getDataObject() return $farm_spec_helper.getDataObject() } + function New-HVPool { <# .Synopsis @@ -2518,6 +3137,78 @@ function New-HVPool { This is a list of tags that access to the desktop is restricted to. No list means that the desktop can be accessed from any connection server. +.PARAMETER PowerPolicy + Power policy for the machines in the desktop after logoff. + This setting is only relevant for managed machines + +.PARAMETER AutomaticLogoffPolicy + Automatically log-off policy after disconnect. + This property has a default value of "NEVER". + +.PARAMETER AutomaticLogoffMinutes + The timeout in minutes for automatic log-off after disconnect. + This property is required if automaticLogoffPolicy is set to "AFTER". + +.PARAMETER AllowUsersToResetMachines + Whether users are allowed to reset/restart their machines. + +.PARAMETER AllowMultipleSessionsPerUser + Whether multiple sessions are allowed per user in case of Floating User Assignment. + +.PARAMETER DeleteOrRefreshMachineAfterLogoff + Whether machines are to be deleted or refreshed after logoff in case of Floating User Assignment. + +.PARAMETER RefreshOsDiskAfterLogoff + Whether and when to refresh the OS disks for dedicated-assignment, linked-clone machines. + +.PARAMETER RefreshPeriodDaysForReplicaOsDisk + Regular interval at which to refresh the OS disk. + +.PARAMETER RefreshThresholdPercentageForReplicaOsDisk + With the 'AT_SIZE' option for refreshOsDiskAfterLogoff, the size of the linked clone's OS disk in the datastore is compared to its maximum allowable size. + +.PARAMETER SupportedDisplayProtocols + The list of supported display protocols for the desktop. + +.PARAMETER DefaultDisplayProtocol + The default display protocol for the desktop. For a managed desktop, this will default to "PCOIP". For an unmanaged desktop, this will default to "RDP". + +.PARAMETER AllowUsersToChooseProtocol + Whether the users can choose the protocol. + +.PARAMETER Renderer3D + Specify 3D rendering dependent types hardware, software, vsphere client etc. + +.PARAMETER EnableGRIDvGPUs + Whether GRIDvGPUs enabled or not + +.PARAMETER VRamSizeMB + VRAM size for View managed 3D rendering. More VRAM can improve 3D performance. + +.PARAMETER MaxNumberOfMonitors + The greater these values are, the more memory will be consumed on the associated ESX hosts + +.PARAMETER MaxResolutionOfAnyOneMonitor + The greater these values are, the more memory will be consumed on the associated ESX hosts. + +.PARAMETER EnableHTMLAccess + HTML Access, enabled by VMware Blast technology, allows users to connect to View machines from Web browsers. + +.PARAMETER Quality + This setting determines the image quality that the flash movie will render. Lower quality results in less bandwidth usage. + +.PARAMETER Throttling + This setting affects the frame rate of the flash movie. If enabled, the frames per second will be reduced based on the aggressiveness level. + +.PARAMETER OverrideGlobalSetting + Mirage configuration specified here will be used for this Desktop + +.PARAMETER Enabled + Whether a Mirage server is enabled. + +.PARAMETER Url + The URL of the Mirage server. This should be in the form "<(DNS name)|(IPv4)|(IPv6)><:(port)>". IPv6 addresses must be enclosed in square brackets. + .PARAMETER Vcenter Virtual Center server-address (IP or FQDN) where the pool virtual machines are located. This should be same as provided to the Connection Server while adding the vCenter server. @@ -2548,10 +3239,69 @@ function New-HVPool { Datastore names to store the VM Applicable to Full, Linked, Instant Clone Pools. +.PARAMETER StorageOvercommit + Storage overcommit determines how View places new VMs on the selected datastores. + Supported values are 'UNBOUNDED','AGGRESSIVE','MODERATE','CONSERVATIVE','NONE' and are case sensitive. + .PARAMETER UseVSAN Whether to use vSphere VSAN. This is applicable for vSphere 5.5 or later. Applicable to Full, Linked, Instant Clone Pools. +.PARAMETER UseSeparateDatastoresReplicaAndOSDisks + Whether to use separate datastores for replica and OS disks. + +.PARAMETER ReplicaDiskDatastore + Datastore to store replica disks for View Composer and Instant clone engine sourced machines. + +.PARAMETER UseNativeSnapshots + Native NFS Snapshots is a hardware feature, specify whether to use or not + +.PARAMETER ReclaimVmDiskSpace + virtual machines can be configured to use a space efficient disk format that supports reclamation of unused disk space. + +.PARAMETER ReclamationThresholdGB + Initiate reclamation when unused space on VM exceeds the threshold. + +.PARAMETER RedirectWindowsProfile + Windows profiles will be redirected to persistent disks, which are not affected by View Composer operations such as refresh, recompose and rebalance. + +.PARAMETER UseSeparateDatastoresPersistentAndOSDisks + Whether to use separate datastores for persistent and OS disks. This must be false if redirectWindowsProfile is false. + +.PARAMETER PersistentDiskDatastores + Name of the Persistent disk datastore + +.PARAMETER PersistentDiskStorageOvercommit + Storage overcommit determines how view places new VMs on the selected datastores. + Supported values are 'UNBOUNDED','AGGRESSIVE','MODERATE','CONSERVATIVE','NONE' and are case sensitive. + +.PARAMETER DiskSizeMB + Size of the persistent disk in MB. + +.PARAMETER DiskDriveLetter + Persistent disk drive letter. + +.PARAMETER RedirectDisposableFiles + Redirect disposable files to a non-persistent disk that will be deleted automatically when a user's session ends. + +.PARAMETER NonPersistentDiskSizeMB + Size of the non persistent disk in MB. + +.PARAMETER NonPersistentDiskDriveLetter + Non persistent disk drive letter. + +.PARAMETER UseViewStorageAccelerator + Whether to use View Storage Accelerator. + +.PARAMETER ViewComposerDiskTypes + Disk types to enable for the View Storage Accelerator feature. + +.PARAMETER RegenerateViewStorageAcceleratorDays + How often to regenerate the View Storage Accelerator cache. + +.PARAMETER BlackoutTimes + A list of blackout times. + .PARAMETER StopOnProvisioningError Set to true to stop provisioning of all VMs on error. Applicable to Full, Linked, Instant Clone Pools. @@ -2637,6 +3387,22 @@ function New-HVPool { The customization spec to use. Applicable to Full, Linked Clone Pools. +.PARAMETER PowerOffScriptName + Power off script. ClonePrep/QuickPrep can run a customization script on instant/linked clone machines before they are powered off. Provide the path to the script on the parent virtual machine. + Applicable to Linked, Instant Clone pools. + +.PARAMETER PowerOffScriptParameters + Power off script parameters. Example: p1 p2 p3 + Applicable to Linked, Instant Clone pools. + +.PARAMETER PostSynchronizationScriptName + Post synchronization script. ClonePrep/QuickPrep can run a customization script on instant/linked clone machines after they are created or recovered or a new image is pushed. Provide the path to the script on the parent virtual machine. + Applicable to Linked, Instant Clone pools. + +.PARAMETER PostSynchronizationScriptParameters + Post synchronization script parameters. Example: p1 p2 p3 + Applicable to Linked, Instant Clone pools. + .PARAMETER Source Source of the Virtual machines for manual pool. Supported values are 'VIRTUAL_CENTER','UNMANAGED'. @@ -2660,37 +3426,41 @@ function New-HVPool { .PARAMETER HvServer Reference to Horizon View Server to query the pools from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer. + first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE + C:\PS>New-HVPool -LinkedClone -PoolName 'vmwarepool' -UserAssignment FLOATING -ParentVM 'Agent_vmware' -SnapshotVM 'kb-hotfix' -VmFolder 'vmware' -HostOrCluster 'CS-1' -ResourcePool 'CS-1' -Datastores 'datastore1' -NamingMethod PATTERN -PoolDisplayName 'vmware linkedclone pool' -Description 'created linkedclone pool from ps' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "vmware2" -MinReady 0 -MaximumCount 1 -SpareCount 1 -ProvisioningTime UP_FRONT -SysPrepName vmwarecust -CustType SYS_PREP -NetBiosName adviewdev -DomainAdmin root Create new automated linked clone pool with naming method pattern - New-HVPool -LinkedClone -PoolName 'vmwarepool' -UserAssignment FLOATING -ParentVM 'Agent_vmware' -SnapshotVM 'kb-hotfix' -VmFolder 'vmware' -HostOrCluster 'CS-1' -ResourcePool 'CS-1' -Datastores 'datastore1' -NamingMethod PATTERN -PoolDisplayName 'vmware linkedclone pool' -Description 'created linkedclone pool from ps' -EnableProvisioning $true -StopOnProvisioningError $false -NamingPattern "vmware2" -MinReady 1 -MaximumCount 1 -SpareCount 1 -ProvisioningTime UP_FRONT -SysPrepName vmwarecust -CustType SYS_PREP -NetBiosName adviewdev -DomainAdmin root .EXAMPLE + New-HVPool -Spec C:\VMWare\Specs\LinkedClone.json -Confirm:$false Create new automated linked clone pool by using JSON spec file - New-HVPool -Spec C:\VMWare\Specs\LinkedClone.json .EXAMPLE - Clone new pool from automated linked (or) full clone pool - Get-HVPool -PoolName 'vmwarepool' | New-HVPool -PoolName 'clonedPool' -NamingPattern 'clonelnk1'; + C:\PS>Get-HVPool -PoolName 'vmwarepool' | New-HVPool -PoolName 'clonedPool' -NamingPattern 'clonelnk1'; (OR) - $vmwarepool = Get-HVPool -PoolName 'vmwarepool'; New-HVPool -ClonePool $vmwarepool -PoolName 'clonedPool' -NamingPattern 'clonelnk1'; + C:\PS>$vmwarepool = Get-HVPool -PoolName 'vmwarepool'; New-HVPool -ClonePool $vmwarepool -PoolName 'clonedPool' -NamingPattern 'clonelnk1'; + Clones new pool by using existing pool configuration .EXAMPLE - Create new automated instant clone pool with naming method pattern New-HVPool -InstantClone -PoolName "InsPoolvmware" -PoolDisplayName "insPool" -Description "create instant pool" -UserAssignment FLOATING -ParentVM 'Agent_vmware' -SnapshotVM 'kb-hotfix' -VmFolder 'vmware' -HostOrCluster 'CS-1' -ResourcePool 'CS-1' -NamingMethod PATTERN -Datastores 'datastore1' -NamingPattern "inspool2" -NetBiosName 'adviewdev' -DomainAdmin root + Create new automated instant clone pool with naming method pattern .EXAMPLE - Create new automated full clone pool with naming method pattern New-HVPool -FullClone -PoolName "FullClone" -PoolDisplayName "FullClonePra" -Description "create full clone" -UserAssignment DEDICATED -Template 'powerCLI-VM-TEMPLATE' -VmFolder 'vmware' -HostOrCluster 'CS-1' -ResourcePool 'CS-1' -Datastores 'datastore1' -NamingMethod PATTERN -NamingPattern 'FullCln1' -SysPrepName vmwarecust -CustType SYS_PREP -NetBiosName adviewdev -DomainAdmin root + Create new automated full clone pool with naming method pattern .EXAMPLE - Create new managed manual pool from virtual center managed VirtualMachines. New-HVPool -MANUAL -PoolName 'manualVMWare' -PoolDisplayName 'MNLPUL' -Description 'Manual pool creation' -UserAssignment FLOATING -Source VIRTUAL_CENTER -VM 'PowerCLIVM1', 'PowerCLIVM2' + Create new managed manual pool from virtual center managed VirtualMachines. .EXAMPLE - Create new unmanaged manual pool from unmanaged VirtualMachines. New-HVPool -MANUAL -PoolName 'unmangedVMWare' -PoolDisplayName 'unMngPl' -Description 'unmanaged Manual Pool creation' -UserAssignment FLOATING -Source UNMANAGED -VM 'myphysicalmachine.vmware.com' + Create new unmanaged manual pool from unmanaged VirtualMachines. + +.EXAMPLE + New-HVPool -spec 'C:\Json\InstantClone.json' -PoolName 'InsPool1'-NamingPattern 'INSPool-' + Creates new instant clone pool by reading few parameters from json and few parameters from command line. .OUTPUTS None @@ -2698,11 +3468,11 @@ function New-HVPool { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -2746,6 +3516,7 @@ function New-HVPool { [Parameter(Mandatory = $true,ParameterSetName = 'FULL_CLONE')] [Parameter(Mandatory = $true,ParameterSetName = 'RDS')] [Parameter(Mandatory = $true,ParameterSetName = 'CLONED_POOL')] + [Parameter(Mandatory = $false,ParameterSetName = 'JSON_FILE')] [string] $PoolName, @@ -2771,7 +3542,6 @@ function New-HVPool { #desktopSpec.automatedDesktopSpec.desktopUserAssignment.userAssigment if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE #desktopSpec.manualDesktopSpec.desktopUserAssignment.userAssigment if MANUAL - [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] @@ -2798,6 +3568,126 @@ function New-HVPool { [string[]] $ConnectionServerRestrictions, + #desktopSpec.desktopSettings.logoffSettings.powerPolicy + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('TAKE_NO_POWER_ACTION', 'ALWAYS_POWERED_ON', 'SUSPEND', 'POWER_OFF')] + [string]$PowerPolicy = 'TAKE_NO_POWER_ACTION', + + #desktopSpec.desktopSettings.logoffSettings.powerPolicy + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('IMMEDIATELY', 'NEVER', 'AFTER')] + [string]$AutomaticLogoffPolicy = 'NEVER', + + #desktopSpec.desktopSettings.logoffSettings.automaticLogoffMinutes + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(1,[int]::MaxValue)] + [int]$AutomaticLogoffMinutes = 120, + + #desktopSpec.desktopSettings.logoffSettings.allowUsersToResetMachines + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean]$allowUsersToResetMachines = $false, + + #desktopSpec.desktopSettings.logoffSettings.allowMultipleSessionsPerUser + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean]$allowMultipleSessionsPerUser = $false, + + #desktopSpec.desktopSettings.logoffSettings.deleteOrRefreshMachineAfterLogoff + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('NEVER', 'DELETE', 'REFRESH')] + [string]$deleteOrRefreshMachineAfterLogoff = 'NEVER', + + #desktopSpec.desktopSettings.logoffSettings.refreshOsDiskAfterLogoff + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('NEVER', 'ALWAYS', 'EVERY', 'AT_SIZE')] + [string]$refreshOsDiskAfterLogoff = 'NEVER', + + #desktopSpec.desktopSettings.logoffSettings.refreshPeriodDaysForReplicaOsDisk + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [int]$refreshPeriodDaysForReplicaOsDisk = 120, + + #desktopSpec.desktopSettings.logoffSettings.refreshThresholdPercentageForReplicaOsDisk + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(1,100)] + [int]$refreshThresholdPercentageForReplicaOsDisk, + + #DesktopDisplayProtocolSettings + #desktopSpec.desktopSettings.logoffSettings.supportedDisplayProtocols + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('RDP', 'PCOIP', 'BLAST')] + [string[]]$supportedDisplayProtocols = @('RDP', 'PCOIP', 'BLAST'), + + #desktopSpec.desktopSettings.logoffSettings.defaultDisplayProtocol + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('RDP', 'PCOIP', 'BLAST')] + [string]$defaultDisplayProtocol = 'PCOIP', + + #desktopSpec.desktopSettings.logoffSettings.allowUsersToChooseProtocol + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [int]$allowUsersToChooseProtocol = $true, + + #desktopSpec.desktopSettings.logoffSettings.enableHTMLAccess + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean]$enableHTMLAccess = $false, + + # DesktopPCoIPDisplaySettings + #desktopSpec.desktopSettings.logoffSettings.pcoipDisplaySettings.renderer3D + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('MANAGE_BY_VSPHERE_CLIENT', 'AUTOMATIC', 'SOFTWARE', 'HARDWARE', 'DISABLED')] + [string]$renderer3D = 'DISABLED', + + #desktopSpec.desktopSettings.logoffSettings.pcoipDisplaySettings.enableGRIDvGPUs + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean]$enableGRIDvGPUs = $false, + + #desktopSpec.desktopSettings.logoffSettings.pcoipDisplaySettings.vRamSizeMB + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(64,512)] + [int]$vRamSizeMB = 96, + + #desktopSpec.desktopSettings.logoffSettings.pcoipDisplaySettings.maxNumberOfMonitors + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(1,4)] + [int]$maxNumberOfMonitors = 2, + + #desktopSpec.desktopSettings.logoffSettings.pcoipDisplaySettings.maxResolutionOfAnyOneMonitor + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('WUXGA', 'WSXGA_PLUS', 'WQXGA', 'UHD')] + [string]$maxResolutionOfAnyOneMonitor = 'WUXGA', + + # flashSettings + #desktopSpec.desktopSettings.flashSettings.quality + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('NO_CONTROL', 'LOW', 'MEDIUM', 'HIGH')] + [string]$quality = 'NO_CONTROL', + + #desktopSpec.desktopSettings.flashSettings.throttling + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateSet('DISABLED', 'CONSERVATIVE', 'MODERATE', 'AGGRESSIVE')] + [string]$throttling = 'DISABLED', + + #mirageConfigurationOverrides + #desktopSpec.desktopSettings.mirageConfigurationOverrides.overrideGlobalSetting + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean]$overrideGlobalSetting = $false, + + #desktopSpec.desktopSettings.mirageConfigurationOverrides.enabled + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean]$enabled = $true, + + #desktopSpec.desktopSettings.mirageConfigurationOverrides.url + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [string]$url = $true, + #desktopSpec.automatedDesktopSpec.virtualCenter if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE #desktopSpec.manualDesktopSpec.virtualCenter if MANUAL [Parameter(Mandatory = $false,ParameterSetName = 'MANUAL')] @@ -2845,6 +3735,13 @@ function New-HVPool { [string] $ResourcePool, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterProvisioningData.datacenter if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] + [string] + $datacenter, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastore if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE [Parameter(Mandatory = $true,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] @@ -2852,12 +3749,127 @@ function New-HVPool { [string[]] $Datastores, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.datastores.storageOvercommit if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] + [string[]] + $StorageOvercommit = $null, + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.useVSAN if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] + [boolean] + $UseVSAN = $false, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.useSeparateDatastoresReplicaAndOSDisks if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [boolean] + $UseSeparateDatastoresReplicaAndOSDisks = $false, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.replicaDiskDatastore if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [string] - $UseVSAN, + $ReplicaDiskDatastore, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.UseNativeSnapshots if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [boolean] + $UseNativeSnapshots = $false, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.reclaimVmDiskSpace if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [boolean] + $ReclaimVmDiskSpace = $false, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.spaceReclamationSettings.reclamationThresholdGB if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(0,[Int]::MaxValue)] + [int] + $ReclamationThresholdGB = 1, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.redirectWindowsProfile if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [boolean] + $RedirectWindowsProfile = $true, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.useSeparateDatastoresPersistentAndOSDisks if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean] + $UseSeparateDatastoresPersistentAndOSDisks = $false, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.PersistentDiskDatastores if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [string[]] + $PersistentDiskDatastores, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.PersistentDiskDatastores if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [string[]] + $PersistentDiskStorageOvercommit = $null, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.diskSizeMB if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(128,[Int]::MaxValue)] + [int] + $DiskSizeMB = 2048, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.persistentDiskSettings.diskDriveLetter if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidatePattern("^[D-Z]$")] + [string] + $DiskDriveLetter = "D", + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.nonPersistentDiskSettings.redirectDisposableFiles if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean] + $redirectDisposableFiles, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.nonPersistentDiskSettings.diskSizeMB if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(512,[Int]::MaxValue)] + [int] + $NonPersistentDiskSizeMB = 4096, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewComposerStorageSettings.nonPersistentDiskSettings.diskDriveLetter if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidatePattern("^[D-Z]|Auto$")] + [string] + $NonPersistentDiskDriveLetter = "Auto", + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewStorageAcceleratorSettings.useViewStorageAccelerator if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [boolean] + $UseViewStorageAccelerator = $false, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewStorageAcceleratorSettings.useViewStorageAccelerator if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [string] + $ViewComposerDiskTypes = "OS_DISKS", + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewStorageAcceleratorSettings.regenerateViewStorageAcceleratorDays if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [ValidateRange(1,999)] + [int] + $RegenerateViewStorageAcceleratorDays = 7, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterStorageSettings.viewStorageAcceleratorSettings.blackoutTimes if LINKED_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [VMware.Hv.DesktopBlackoutTime[]] + $BlackoutTimes, + + #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.virtualCenterNetworkingSettings.nics + [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] + [VMware.Hv.DesktopNetworkInterfaceCardSettings[]] + $Nics, #desktopSpec.automatedDesktopSpec.virtualCenterProvisioningSettings.enableProvsioning if LINKED_CLONE, INSTANT_CLONE, FULL_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] @@ -2894,6 +3906,7 @@ function New-HVPool { [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] [Parameter(Mandatory = $false,ParameterSetName = 'CLONED_POOL')] + [Parameter(Mandatory = $false,ParameterSetName = 'JSON_FILE')] [string] $NamingPattern = $poolName + '{n:fixed=4}', @@ -2954,8 +3967,9 @@ function New-HVPool { [int] $NumUnassignedMachinesKeptPoweredOn = 1, - #desktopSpec.automatedDesktopSpec.customizationSettings.cloneprepCustomizationSettings.instantCloneEngineDomainAdministrator if INSTANT_CLONE + #desktopSpec.automatedDesktopSpec.customizationSettings.AdContainer [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] $AdContainer = 'CN=Computers', [Parameter(Mandatory = $true,ParameterSetName = 'INSTANT_CLONE')] @@ -2963,6 +3977,8 @@ function New-HVPool { [Parameter(Mandatory = $false,ParameterSetName = 'FULL_CLONE')] [string]$NetBiosName, + #desktopSpec.automatedDesktopSpec.customizationSettings.domainAdministrator + #desktopSpec.automatedDesktopSpec.customizationSettings.cloneprepCustomizationSettings.instantCloneEngineDomainAdministrator [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] [string]$DomainAdmin = $null, @@ -2974,12 +3990,51 @@ function New-HVPool { [string] $CustType, + #desktopSpec.automatedDesktopSpec.customizationSettings.reusePreExistingAccounts if LINKED_CLONE, INSTANT_CLONE + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [Boolean] + $ReusePreExistingAccounts = $false, + #desktopSpec.automatedDesktopSpec.customizationSettings.sysprepCustomizationSettings.customizationSpec if LINKED_CLONE, FULL_CLONE [Parameter(Mandatory = $false,ParameterSetName = "LINKED_CLONE")] [Parameter(Mandatory = $false,ParameterSetName = "FULL_CLONE")] [string] $SysPrepName, + #desktopSpec.automatedDesktopSpec.customizationSettings.noCustomizationSettings.doNotPowerOnVMsAfterCreation if FULL_CLONE + [Parameter(Mandatory = $false,ParameterSetName = "FULL_CLONE")] + [boolean] + $DoNotPowerOnVMsAfterCreation = $false, + + #desktopSpec.automatedDesktopSpec.customizationSettings.quickprepCustomizationSettings.powerOffScriptName if LINKED_CLONE, INSTANT_CLONE + #desktopSpec.automatedDesktopSpec.customizationSettings.cloneprepCustomizationSettings.powerOffScriptName + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [string] + $PowerOffScriptName, + + #desktopSpec.automatedDesktopSpec.customizationSettings.quickprepCustomizationSettings.powerOffScriptParameters + #desktopSpec.automatedDesktopSpec.customizationSettings.cloneprepCustomizationSettings.powerOffScriptParameters + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [string] + $PowerOffScriptParameters, + + #desktopSpec.automatedDesktopSpec.customizationSettings.quickprepCustomizationSettings.postSynchronizationScriptName + #desktopSpec.automatedDesktopSpec.customizationSettings.cloneprepCustomizationSettings.postSynchronizationScriptName + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [string] + $PostSynchronizationScriptName, + + #desktopSpec.automatedDesktopSpec.customizationSettings.quickprepCustomizationSettings.postSynchronizationScriptParameters + #desktopSpec.automatedDesktopSpec.customizationSettings.cloneprepCustomizationSettings.postSynchronizationScriptParameters + [Parameter(Mandatory = $false,ParameterSetName = 'INSTANT_CLONE')] + [Parameter(Mandatory = $false,ParameterSetName = 'LINKED_CLONE')] + [string] + $PostSynchronizationScriptParameters, + #manual desktop [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [ValidateSet('VIRTUAL_CENTER','UNMANAGED')] @@ -2988,10 +4043,13 @@ function New-HVPool { [Parameter(Mandatory = $true,ParameterSetName = 'MANUAL')] [Parameter(Mandatory = $false,ParameterSetName = "JSON_FILE")] + [Parameter(Mandatory = $false,ParameterSetName = 'CLONED_POOL')] [string[]]$VM, #farm [Parameter(Mandatory = $false,ParameterSetName = 'RDS')] + [Parameter(Mandatory = $false,ParameterSetName = 'CLONED_POOL')] + [string] $Farm, @@ -3057,10 +4115,10 @@ function New-HVPool { } process { - + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys if ($poolName) { try { - $sourcePool = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer + $sourcePool = Get-HVPoolSummary -poolName $poolName -suppressInfo $true -hvServer $hvServer } catch { Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" break @@ -3078,6 +4136,14 @@ function New-HVPool { Write-Error "Json file exception, $_" break } + + try { + #Json object validation + Test-HVPoolSpec -PoolObject $jsonObject + } catch { + Write-Error "Json object validation failed, $_" + break + } if ($jsonObject.type -eq "AUTOMATED") { $poolType = 'AUTOMATED' if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenter) { @@ -3094,18 +4160,43 @@ function New-HVPool { $custType = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CustomizationType if ($jsonObject.AutomatedDesktopSpec.ProvisioningType -eq "INSTANT_CLONE_ENGINE") { $InstantClone = $true + if ($null -ne $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings) { + $domainAdmin = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.InstantCloneEngineDomainAdministrator + $powerOffScriptName = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.PowerOffScriptName + $powerOffScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.PowerOffScriptParameters + $postSynchronizationScriptName = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.PostSynchronizationScriptName + $postSynchronizationScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.PostSynchronizationScriptParameters + } } else { if ($jsonObject.AutomatedDesktopSpec.ProvisioningType -eq "VIEW_COMPOSER") { $LinkedClone = $true - } else { + $domainAdmin = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.domainAdministrator + } elseIf($jsonObject.AutomatedDesktopSpec.ProvisioningType -eq "VIRTUAL_CENTER") { $FullClone = $true } - $sysPrepName = $jsonObject.SysPrepName + switch ($custType) { + 'SYS_PREP' { + $sysprepCustomizationSettings = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.SysprepCustomizationSettings + $sysPrepName = $sysprepCustomizationSettings.customizationSpec + $reusePreExistingAccounts = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.reusePreExistingAccounts + } + 'QUICK_PREP' { + $powerOffScriptName = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PowerOffScriptName + $powerOffScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PowerOffScriptParameters + $postSynchronizationScriptName = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PostSynchronizationScriptName + $postSynchronizationScriptParameters = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.PostSynchronizationScriptParameters + } + 'NONE' { + $doNotPowerOnVMsAfterCreation = $jsonObject.AutomatedDesktopSpec.CustomizationSettings.NoCustomizationSettings.DoNotPowerOnVMsAfterCreation + } + } } $namingMethod = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod $transparentPageSharingScope = $jsonObject.AutomatedDesktopSpec.virtualCenterManagedCommonSettings.TransparentPageSharingScope if ($namingMethod -eq "PATTERN") { - $namingPattern = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings.namingPattern + if ($NamingPattern -eq '{n:fixed=4}') { + $namingPattern = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings.namingPattern + } $maximumCount = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings.maxNumberOfMachines $spareCount = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings.numberOfSpareMachines $provisioningTime = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings.provisioningTime @@ -3114,6 +4205,9 @@ function New-HVPool { $startInMaintenanceMode = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.SpecificNamingSpec.startMachinesInMaintenanceMode $numUnassignedMachinesKeptPoweredOn = $jsonObject.AutomatedDesktopSpec.VmNamingSpec.SpecificNamingSpec.numUnassignedMachinesKeptPoweredOn } + $enableProvisioning = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.EnableProvisioning + $stopProvisioningOnError = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.stopProvisioningOnError + $minReady = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.Template) { $template = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.Template } @@ -3123,14 +4217,87 @@ function New-HVPool { if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.Snapshot) { $snapshotVM = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.Snapshot } + $dataCenter = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.dataCenter $vmFolder = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.VmFolder $hostOrCluster = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.HostOrCluster $resourcePool = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.ResourcePool $dataStoreList = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.Datastores foreach ($dtStore in $dataStoreList) { $datastores += $dtStore.Datastore + $storageOvercommit += $dtStore.StorageOvercommit } - } elseif ($jsonObject.type -eq "MANUAL") { + $useVSan = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.UseVSan + if ($LinkedClone -or $InstantClone) { + $useSeparateDatastoresReplicaAndOSDisks = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.UseSeparateDatastoresReplicaAndOSDisks + if ($useSeparateDatastoresReplicaAndOSDisks) { + $replicaDiskDatastore = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.replicaDiskDatastore + } + if ($LinkedClone) { + #For Instant clone desktops, this setting can only be set to false + $useNativeSnapshots = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.useNativeSnapshots + $reclaimVmDiskSpace = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.spaceReclamationSettings.reclaimVmDiskSpace + if ($reclaimVmDiskSpace) { + $reclamationThresholdGB = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.spaceReclamationSettings.reclamationThresholdGB + } + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings) { + $redirectWindowsProfile = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.RedirectWindowsProfile + if ($redirectWindowsProfile) { + $useSeparateDatastoresPersistentAndOSDisks = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.UseSeparateDatastoresPersistentAndOSDisks + } + $dataStoreList = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.persistentDiskDatastores + foreach ($dtStore in $dataStoreList) { + $persistentDiskDatastores += $dtStore.Datastore + $PersistentDiskStorageOvercommit += $dtStore.StorageOvercommit + } + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.DiskSizeMB) { + $diskSizeMB = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.DiskSizeMB + } + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.DiskDriveLetter) { + $diskDriveLetter = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.PersistentDiskSettings.DiskDriveLetter + } + } + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.nonPersistentDiskSettings) { + $redirectDisposableFiles = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.NonPersistentDiskSettings.RedirectDisposableFiles + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.NonPersistentDiskSettings.DiskSizeMB) { + $nonPersistentDiskSizeMB = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.NonPersistentDiskSettings.DiskSizeMB + } + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.NonPersistentDiskSettings.DiskDriveLetter) { + $nonPersistentDiskDriveLetter = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings.NonPersistentDiskSettings.DiskDriveLetter + } + } + } else { + $useNativeSnapshots = $false + $redirectWindowsProfile = $false + } + } + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.viewStorageAcceleratorSettings) { + $useViewStorageAccelerator = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.viewStorageAcceleratorSettings.UseViewStorageAccelerator + if ($useViewStorageAccelerator -and $LinkedClone) { + $viewComposerDiskTypes = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.viewStorageAcceleratorSettings.ViewComposerDiskTypes + } + if (! $InstantClone -and $useViewStorageAccelerator) { + $regenerateViewStorageAcceleratorDays = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.viewStorageAcceleratorSettings.RegenerateViewStorageAcceleratorDays + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.viewStorageAcceleratorSettings.blackoutTimes) { + $blackoutTimesList =$jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.viewStorageAcceleratorSettings.blackoutTimes + foreach ($blackout in $blackoutTimesList) { + $blackoutObj = New-Object VMware.Hv.DesktopBlackoutTime + $blackoutObj.Days = $blackout.Days + $blackoutObj.StartTime = $blackout.StartTime + $blackoutObj.EndTime = $blackout.EndTime + $blackoutTimes += $blackoutObj + } + } + } + } + <# ToDo Nic + if ($null -ne $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.nics) { + $nicList = $jsonObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.nics + foreach($nicObj in $nicList) { + $nic = New-Object VMware.Hv.DesktopNetworkInterfaceCardSettings + } + } + #> + } elseIf ($jsonObject.type -eq "MANUAL") { $MANUAL = $true $poolType = 'MANUAL' $userAssignment = $jsonObject.ManualDesktopSpec.userAssignment.userAssignment @@ -3149,21 +4316,87 @@ function New-HVPool { $poolDisplayName = $jsonObject.base.DisplayName $description = $jsonObject.base.Description $accessGroup = $jsonObject.base.AccessGroup - $poolName = $jsonObject.base.name + if (!$poolName) { + $poolName = $jsonObject.base.name + } + + <# + # Populate desktop settings + #> + if ($null -ne $jsonObject.DesktopSettings) { + $Enable = $jsonObject.DesktopSettings.enabled + $deleting = $jsonObject.DesktopSettings.deleting + if ($null -ne $jsonObject.DesktopSettings.connectionServerRestrictions) { + $ConnectionServerRestrictions = $jsonObject.DesktopSettings.connectionServerRestrictions + } + if ($poolType -ne 'RDS') { + if ($null -ne $jsonObject.DesktopSettings.logoffSettings) { + $powerPolicy = $jsonObject.DesktopSettings.logoffSettings.powerPolicy + $automaticLogoffPolicy = $jsonObject.DesktopSettings.logoffSettings.automaticLogoffPolicy + if ($null -ne $jsonObject.DesktopSettings.logoffSettings.automaticLogoffMinutes) { + $automaticLogoffMinutes = $jsonObject.DesktopSettings.logoffSettings.automaticLogoffMinutes + } + $allowUsersToResetMachines = $jsonObject.DesktopSettings.logoffSettings.allowUsersToResetMachines + $allowMultipleSessionsPerUser = $jsonObject.DesktopSettings.logoffSettings.allowMultipleSessionsPerUser + $deleteOrRefreshMachineAfterLogoff = $jsonObject.DesktopSettings.logoffSettings.deleteOrRefreshMachineAfterLogoff + $refreshOsDiskAfterLogoff = $jsonObject.DesktopSettings.logoffSettings.refreshOsDiskAfterLogoff + if ($jsonObject.DesktopSettings.logoffSettings.refreshPeriodDaysForReplicaOsDisk) { + $refreshPeriodDaysForReplicaOsDisk = $jsonObject.DesktopSettings.logoffSettings.refreshPeriodDaysForReplicaOsDisk + } + if ($jsonObject.DesktopSettings.logoffSettings.refreshThresholdPercentageForReplicaOsDisk) { + $refreshThresholdPercentageForReplicaOsDisk = $jsonObject.DesktopSettings.logoffSettings.refreshThresholdPercentageForReplicaOsDisk + } + } + + if ($null -ne $jsonObject.DesktopSettings.displayProtocolSettings) { + $supportedDisplayProtocols = $jsonObject.DesktopSettings.displayProtocolSettings.supportedDisplayProtocols + $defaultDisplayProtocol = $jsonObject.DesktopSettings.displayProtocolSettings.defaultDisplayProtocol + $allowUsersToChooseProtocol = $jsonObject.DesktopSettings.displayProtocolSettings.allowUsersToChooseProtocol + if ($null -ne $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings) { + $renderer3D = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.renderer3D + $enableGRIDvGPUs = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.enableGRIDvGPUs + if ($jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.vRamSizeMB) { + $vRamSizeMB = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.vRamSizeMB + } + $maxNumberOfMonitors = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.maxNumberOfMonitors + $maxResolutionOfAnyOneMonitor = $jsonObject.DesktopSettings.displayProtocolSettings.pcoipDisplaySettings.maxResolutionOfAnyOneMonitor + } + $enableHTMLAccess = $jsonObject.DesktopSettings.displayProtocolSettings.enableHTMLAccess + } + + if ($null -ne $jsonObject.DesktopSettings.mirageConfigurationOverrides) { + $overrideGlobalSetting = $jsonObject.DesktopSettings.mirageConfigurationOverrides.overrideGlobalSetting + if ($jsonObject.DesktopSettings.mirageConfigurationOverrides.enabled) { + $enabled = $jsonObject.DesktopSettings.mirageConfigurationOverrides.enabled + } + if ($jsonObject.DesktopSettings.mirageConfigurationOverrides.url) { + $url = $jsonObject.DesktopSettings.mirageConfigurationOverrides.url + } + } + } + if ($null -ne $jsonObject.DesktopSettings.flashSettings) { + $quality = $jsonObject.DesktopSettings.flashSettings.quality + $throttling = $jsonObject.DesktopSettings.flashSettings.throttling + } + #desktopsettings ends + } + if ($null -ne $jsonObject.GlobalEntitlementData) { + $globalEntitlement = $jsonObject.GlobalEntitlementData.globalEntitlement + } } if ($PSCmdlet.MyInvocation.ExpectingInput -or $clonePool) { if ($clonePool -and ($clonePool.GetType().name -eq 'DesktopSummaryView')) { $clonePool = Get-HVPool -poolName $clonePool.desktopsummarydata.name - } elseif (!($clonePool -and ($clonePool.GetType().name -eq 'DesktopInfo'))) { + } elseIf (!($clonePool -and ($clonePool.GetType().name -eq 'DesktopInfo'))) { Write-Error "In pipeline did not get object of expected type DesktopSummaryView/DesktopInfo" return } $poolType = $clonePool.type $desktopBase = $clonePool.base $desktopSettings = $clonePool.DesktopSettings - $provisioningType = $null + $provisioningType = $clonePool.source if ($clonePool.AutomatedDesktopData) { $provisioningType = $clonePool.AutomatedDesktopData.ProvisioningType $virtualCenterID = $clonePool.AutomatedDesktopData.VirtualCenter @@ -3173,11 +4406,30 @@ function New-HVPool { $DesktopVirtualCenterProvisioningData = $DesktopVirtualCenterProvisioningSettings.VirtualCenterProvisioningData $DesktopVirtualCenterStorageSettings = $DesktopVirtualCenterProvisioningSettings.VirtualCenterStorageSettings $DesktopVirtualCenterNetworkingSettings = $DesktopVirtualCenterProvisioningSettings.VirtualCenterNetworkingSettings - $desktopVirtualCenterManagedCommonSettings = $clonePool.AutomatedDesktopData.virtualCenterManagedCommonSettings - $desktopCustomizationSettings = $clonePool.AutomatedDesktopData.CustomizationSettings + $DesktopVirtualCenterManagedCommonSettings = $clonePool.AutomatedDesktopData.virtualCenterManagedCommonSettings + $DesktopCustomizationSettings = $clonePool.AutomatedDesktopData.CustomizationSettings + $CurrentImageState =` + $clonePool.AutomatedDesktopData.provisioningStatusData.instantCloneProvisioningStatusData.instantCloneCurrentImageState } - if (($null -eq $provisioningType) -or ($provisioningType -eq 'INSTANT_CLONE_ENGINE')) { - Write-Error "Only Automated linked clone or full clone pool support cloning" + elseIf ($clonePool.ManualDesktopData) { + if (! $VM) { + Write-Error "ManualDesktop pool cloning requires list of machines, parameter VM is empty" + break + } + $source = $clonePool.source + $virtualCenterID = $clonePool.ManualDesktopData.VirtualCenter + $desktopUserAssignment = $clonePool.ManualDesktopData.userAssignment + $desktopVirtualCenterStorageSettings = $clonePool.ManualDesktopData.viewStorageAcceleratorSettings + $desktopVirtualCenterManagedCommonSettings = $clonePool.ManualDesktopData.virtualCenterManagedCommonSettings + } + elseIf($clonePool.RdsDesktopData) { + if (! $Farm) { + Write-Error "RdsDesktop pool cloning requires farm, parameter Farm is not set" + break + } + } + if ($provisioningType -eq 'INSTANT_CLONE_ENGINE' -and $poolType -eq 'AUTOMATED' -and $CurrentImageState -ne 'READY') { + Write-Error "Instant clone pool's Current Image State should be in 'READY' state, otherwise cloning is not supported" break } } else { @@ -3186,19 +4438,19 @@ function New-HVPool { $poolType = 'AUTOMATED' $provisioningType = 'INSTANT_CLONE_ENGINE' } - elseif ($LinkedClone) { + elseIf ($LinkedClone) { $poolType = 'AUTOMATED' $provisioningType = 'VIEW_COMPOSER' } - elseif ($FullClone) { + elseIf ($FullClone) { $poolType = 'AUTOMATED' $provisioningType = 'VIRTUAL_CENTER' } - elseif ($Manual) { $poolType = 'MANUAL' } - elseif ($RDS) { $poolType = 'RDS' } + elseIf ($Manual) { $poolType = 'MANUAL' } + elseIf ($RDS) { $poolType = 'RDS' } } - $script:desktopSpecObj = Get-HVDesktopSpec -poolType $poolType -provisioningType $provisioningType -namingMethod $namingMethod + $script:desktopSpecObj = Get-DesktopSpec -poolType $poolType -provisioningType $provisioningType -namingMethod $namingMethod # # accumulate properties that are shared among various type @@ -3256,8 +4508,8 @@ function New-HVPool { { 'RDS' { <# - Query FarmId from Farm Name - #> + Query FarmId from Farm Name + #> $QueryFilterEquals = New-Object VMware.Hv.QueryFilterEquals $QueryFilterEquals.memberName = 'data.name' $QueryFilterEquals.value = $farm @@ -3289,6 +4541,9 @@ function New-HVPool { $machineList = Get-RegisteredPhysicalMachine -services $services -machinesList $VM } $desktopSpecObj.ManualDesktopSpec.Machines = $machineList + if ($desktopUserAssignment) { + $desktopSpecObj.ManualDesktopSpec.userAssignment = $desktopUserAssignment + } } default { if (!$desktopVirtualMachineNamingSpec) { @@ -3332,7 +4587,9 @@ function New-HVPool { try { $desktopVirtualCenterProvisioningData = Get-HVPoolProvisioningData -vc $virtualCenterID -vmObject $desktopVirtualCenterProvisioningData $hostClusterId = $desktopVirtualCenterProvisioningData.HostOrCluster - $desktopVirtualCenterStorageSettings = Get-HVPoolStorageObject -hostclusterID $hostClusterId -storageObject $desktopVirtualCenterStorageSettings + $hostOrCluster_helper = New-Object VMware.Hv.HostOrClusterService + $hostClusterIds = (($hostOrCluster_helper.HostOrCluster_GetHostOrClusterTree($services, $desktopVirtualCenterProvisioningData.datacenter)).treeContainer.children.info).Id + $desktopVirtualCenterStorageSettings = Get-HVPoolStorageObject -hostClusterIds $hostClusterId -storageObject $desktopVirtualCenterStorageSettings $DesktopVirtualCenterNetworkingSettings = Get-HVPoolNetworkSetting -networkObject $DesktopVirtualCenterNetworkingSettings $desktopCustomizationSettings = Get-HVPoolCustomizationSetting -vc $virtualCenterID -customObject $desktopCustomizationSettings } catch { @@ -3341,10 +4598,10 @@ function New-HVPool { break } - if (!$DesktopVirtualCenterProvisioningSettings) { - $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.enableProvisioning = $true - $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.stopProvisioningOnError = $true - $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance = 0 + if (! $DesktopVirtualCenterProvisioningSettings) { + $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.enableProvisioning = $enableProvisioning + $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.stopProvisioningOnError = $stopProvisioningOnError + $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.minReadyVMsOnVComposerMaintenance = $minReady $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData = $desktopVirtualCenterProvisioningData $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings = $desktopVirtualCenterStorageSettings $desktopSpecObj.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterNetworkingSettings = $DesktopVirtualCenterNetworkingSettings @@ -3374,8 +4631,7 @@ function New-HVPool { } if (!$desktopBase) { $accessGroup_client = New-Object VMware.Hv.AccessGroupService - $ag = $accessGroup_client.AccessGroup_List($services) | Where-Object { $_.base.name -eq $accessGroup } - $desktopSpecObj.base.AccessGroup = $ag.id + $desktopSpecObj.base.AccessGroup = Get-HVAccessGroupID $accessGroup_client.AccessGroup_List($services) } else { $desktopSpecObj.base = $desktopBase } @@ -3385,8 +4641,83 @@ function New-HVPool { $desktopSpecObj.base.Description = $description $desktopSpecObj.type = $poolType - if ($desktopSettings) { $desktopSpecObj.DesktopSettings = $desktopSettings } + if (! $desktopSettings) { + $desktopSettingsService = New-Object VMware.Hv.DesktopService + $desktopSettingsHelper = $desktopSettingsService.getDesktopSettingsHelper() + $desktopSettingsHelper.setEnabled($Enable) + $desktopSettingsHelper.setConnectionServerRestrictions($ConnectionServerRestrictions) + #$desktopLogoffSettings = New-Object VMware.Hv.DesktopLogoffSettings + $desktopLogoffSettings = $desktopSettingsService.getDesktopLogoffSettingsHelper() + if ($InstantClone) { + $deleteOrRefreshMachineAfterLogoff = "DELETE" + $powerPolicy = "ALWAYS_POWERED_ON" + } + $desktopLogoffSettings.setPowerPolicy($powerPolicy) + $desktopLogoffSettings.setAutomaticLogoffPolicy($automaticLogoffPolicy) + $desktopLogoffSettings.setAutomaticLogoffMinutes($automaticLogoffMinutes) + $desktopLogoffSettings.setAllowUsersToResetMachines($allowUsersToResetMachines) + $desktopLogoffSettings.setAllowMultipleSessionsPerUser($allowMultipleSessionsPerUser) + $desktopLogoffSettings.setDeleteOrRefreshMachineAfterLogoff($deleteOrRefreshMachineAfterLogoff) + $desktopLogoffSettings.setRefreshOsDiskAfterLogoff($refreshOsDiskAfterLogoff) + $desktopLogoffSettings.setRefreshPeriodDaysForReplicaOsDisk($refreshPeriodDaysForReplicaOsDisk) + if ($refreshThresholdPercentageForReplicaOsDisk -and $refreshOsDiskAfterLogoff -eq "AT_SIZE") { + $desktopLogoffSettings.setRefreshThresholdPercentageForReplicaOsDisk($refreshThresholdPercentageForReplicaOsDisk) + } + if ($poolType -ne 'RDS') { + $desktopSettingsHelper.setLogoffSettings($desktopLogoffSettings.getDataObject()) + + $desktopDisplayProtocolSettings = $desktopSettingsService.getDesktopDisplayProtocolSettingsHelper() + #setSupportedDisplayProtocols is not exists, because this property cannot be updated. + $desktopDisplayProtocolSettings.getDataObject().SupportedDisplayProtocols = $supportedDisplayProtocols + $desktopDisplayProtocolSettings.setDefaultDisplayProtocol($defaultDisplayProtocol) + $desktopDisplayProtocolSettings.setEnableHTMLAccess($enableHTMLAccess) + $desktopDisplayProtocolSettings.setAllowUsersToChooseProtocol($allowUsersToChooseProtocol) + + $desktopPCoIPDisplaySettings = $desktopSettingsService.getDesktopPCoIPDisplaySettingsHelper() + $desktopPCoIPDisplaySettings.setRenderer3D($renderer3D) + #setEnableGRIDvGPUs is not exists, because this property cannot be updated. + $desktopPCoIPDisplaySettings.getDataObject().EnableGRIDvGPUs = $enableGRIDvGPUs + $desktopPCoIPDisplaySettings.setVRamSizeMB($vRamSizeMB) + $desktopPCoIPDisplaySettings.setMaxNumberOfMonitors($maxNumberOfMonitors) + $desktopPCoIPDisplaySettings.setMaxResolutionOfAnyOneMonitor($maxResolutionOfAnyOneMonitor) + $desktopDisplayProtocolSettings.setPcoipDisplaySettings($desktopPCoIPDisplaySettings.getDataObject()) + $desktopSettingsHelper.setDisplayProtocolSettings($desktopDisplayProtocolSettings.getDataObject()) + + $desktopMirageConfigOverrides = $desktopSettingsService.getDesktopMirageConfigurationOverridesHelper() + $desktopMirageConfigOverrides.setEnabled($enabled) + $desktopMirageConfigOverrides.setOverrideGlobalSetting($overrideGlobalSetting) + $desktopMirageConfigOverrides.setUrl($url) + $desktopSettingsHelper.setMirageConfigurationOverrides($desktopMirageConfigOverrides.getDataObject()) + $desktopSettings = $desktopSettingsHelper.getDataObject() + } + $desktopFlashSettings = $desktopSettingsService.getDesktopAdobeFlashSettingsHelper() + $desktopFlashSettings.setQuality($quality) + $desktopFlashSettings.setThrottling($throttling) + $desktopSettingsHelper.setFlashSettings($desktopFlashSettings.getDataObject()) + } + + $desktopSpecObj.DesktopSettings = $desktopSettings + $info = $services.PodFederation.PodFederation_get() + if ($globalEntitlement -and ("ENABLED" -eq $info.localPodStatus.status)) { + $QueryFilterEquals = New-Object VMware.Hv.QueryFilterEquals + $QueryFilterEquals.memberName = 'base.displayName' + $QueryFilterEquals.value = $globalEntitlement + $defn = New-Object VMware.Hv.QueryDefinition + $defn.queryEntityType = 'GlobalEntitlementSummaryView' + $defn.Filter = $QueryFilterEquals + $query_service_helper = New-Object VMware.Hv.QueryServiceService + try { + $queryResults = $query_service_helper.QueryService_Query($services,$defn) + $globalEntitlementid = $queryResults.Results.id + if ($globalEntitlementid.length -eq 1) { + $desktopGlobalEntitlementData = New-Object VMware.Hv.DesktopGlobalEntitlementData -Property @{'globalEntitlement'= $globalEntitlementid;} + } + } + catch { + Write-Host "GlobalEntitlement " $_ + } + } if ($desktopAutomatedDesktopSpec) { $desktopSpecObj.AutomatedDesktopSpec = $desktopAutomatedDesktopSpec } @@ -3396,18 +4727,114 @@ function New-HVPool { # Please uncomment below code, if you want save desktopSpec object to json file <# - $myDebug = convertto-json -InputObject $desktopSpecObj -depth 12 - $myDebug | out-file -filepath c:\temp\copieddesktop.json - #> + $myDebug = convertto-json -InputObject $desktopSpecObj -depth 12 + $myDebug | out-file -filepath c:\temp\copieddesktop.json + #> $desktop_helper = New-Object VMware.Hv.DesktopService - $desktop_helper.Desktop_create($services,$desktopSpecObj) + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($desktopSpecObj.base.name)) { + $id = $desktop_helper.Desktop_create($services,$desktopSpecObj) + } else { + try { + #DesktopSpec validation + Test-HVPoolSpec -PoolObject $desktopSpecObj + } catch { + Write-Error "DesktopSpec object validation failed, $_" + break + } + } + return $desktopSpecObj } end { $desktopSpecObj = $null [System.gc]::collect() } +} +function Get-HVResourceStructure { +<# +.Synopsis + Output the structure of the resource pools available to a HV. Primarily this is for debugging + + PS> Get-HVResourceStructure + vCenter vc.domain.local + Container DC path /DC/host + HostOrCluster Servers path /DC/host/Servers + HostOrCluster VDI path /DC/host/VDI + ResourcePool Servers path /DC/host/Servers/Resources + ResourcePool VDI path /DC/host/VDI/Resources + ResourcePool RP1 path /DC/host/VDI/Resources/RP1 + ResourcePool RP2 path /DC/host/VDI/Resources/RP1/RP2 + + Author : Mark Elvers +#> + param( + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $HvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $vc_service_helper = New-Object VMware.Hv.VirtualCenterService + $vcList = $vc_service_helper.VirtualCenter_List($services) + foreach ($vc in $vcList) { + Write-Host vCenter $vc.ServerSpec.ServerName + $datacenterList = @{} + $BaseImage_service_helper = New-Object VMware.Hv.BaseImageVmService + $parentList = $BaseImage_service_helper.BaseImageVm_List($services, $vc.id) + foreach ($possibleParent in $parentList) { + if (-not $datacenterList.ContainsKey($possibleParent.datacenter.id)) { + $datacenterList.Add($possibleParent.datacenter.id, $possibleParent.datacenter) + } + if (0) { + Write-Host "$($possibleParent.name): " -NoNewLine + if ($possibleParent.incompatibleReasons.inUseByDesktop) { Write-Host "inUseByDesktop, " -NoNewLine } + if ($possibleParent.incompatibleReasons.viewComposerReplica) { Write-Host "viewComposerReplica, " -NoNewLine } + if ($possibleParent.incompatibleReasons.inUseByLinkedCloneDesktop) { Write-Host "inUseByLinkedCloneDesktop, " -NoNewLine } + if ($possibleParent.incompatibleReasons.unsupportedOSForLinkedCloneFarm) { Write-Host "unsupportedOSForLinkedCloneFarm, " -NoNewLine } + if ($possibleParent.incompatibleReasons.unsupportedOS) { Write-Host "unsupportedOS, " -NoNewLine } + if ($possibleParent.incompatibleReasons.noSnapshots) { Write-Host "noSnapshots, " -NoNewLine } + Write-Host + } + } + $hcNodes = @() + $index = 0 + foreach ($datacenter in $datacenterList.keys) { + $HostOrCluster_service_helper = New-Object VMware.Hv.HostOrClusterService + $hcNodes += $HostOrCluster_service_helper.HostOrCluster_GetHostOrClusterTree($services, $datacenterList.$datacenter) + while ($index -lt $hcNodes.length) { + if ($hcNodes[$index].container) { + Write-Host "Container" $hcNodes[$index].treecontainer.name "path" $hcNodes[$index].treecontainer.path + if ($hcNodes[$index].treecontainer.children.Length) { $hcNodes += $hcNodes[$index].treecontainer.children } + } else { + Write-Host "HostOrCluster" $hcNodes[$index].info.name "path" $hcNodes[$index].info.path + } + $index++ + } + } + $rpNodes = @() + $index = 0 + foreach ($hostOrCluster in $hcNodes) { + if (-not $hostOrCluster.container) { + $ResourcePool_service_helper = New-Object VMware.Hv.ResourcePoolService + $rpNodes += $ResourcePool_service_helper.ResourcePool_GetResourcePoolTree($services, $hostOrCluster.info.id) + while ($index -lt $rpNodes.length) { + Write-Host "ResourcePool" $rpNodes[$index].resourcePoolData.name "path" $rpNodes[$index].resourcePoolData.path + if ($rpNodes[$index].children.Length) { $rpNodes += $rpNodes[$index].children } + $index++ + } + } + } + } + } + end { + [System.gc]::collect() + } } function Get-HVPoolProvisioningData { @@ -3428,6 +4855,12 @@ function Get-HVPoolProvisioningData { } $vmObject.Template = $templateVM.id $dataCenterID = $templateVM.datacenter + if ($dataCenter -and $dataCenterID) { + $VmTemplateInfo = $vm_template_helper.VmTemplate_ListByDatacenter($dataCenterID) + if (! ($VmTemplateInfo.Path -like "/$dataCenter/*")) { + throw "$template not exists in datacenter: [$dataCenter]" + } + } $vmObject.datacenter = $dataCenterID } if ($parentVM) { @@ -3457,7 +4890,9 @@ function Get-HVPoolProvisioningData { $folderList += $folders while ($folderList.Length -gt 0) { $item = $folderList[0] - if ($item -and !$_.folderdata.incompatiblereasons.inuse -and !$_.folderdata.incompatiblereasons.viewcomposerreplicafolder -and ($item.folderdata.name -eq $vmFolder)) { + if ($item -and !$_.folderdata.incompatiblereasons.inuse -and ` + !$_.folderdata.incompatiblereasons.viewcomposerreplicafolder -and ` + (($item.folderdata.path -eq $vmFolder) -or ($item.folderdata.name -eq $vmFolder))) { $vmObject.VmFolder = $item.id break } @@ -3472,47 +4907,196 @@ function Get-HVPoolProvisioningData { } if ($hostOrCluster) { $vmFolder_helper = New-Object VMware.Hv.HostOrClusterService - $hostClusterList = ($vmFolder_helper.HostOrCluster_GetHostOrClusterTree($services,$vmobject.datacenter)).treeContainer.children.info - $hostClusterObj = $hostClusterList | Where-Object { $_.name -eq $hostOrCluster } - if ($null -eq $hostClusterObj) { + $vmObject.HostOrCluster = Get-HVHostOrClusterID $vmFolder_helper.HostOrCluster_GetHostOrClusterTree($services,$vmobject.datacenter) + if ($null -eq $vmObject.HostOrCluster) { throw "No hostOrCluster found with Name: [$hostOrCluster]" } - $vmObject.HostOrCluster = $hostClusterObj.id } if ($resourcePool) { $resourcePool_helper = New-Object VMware.Hv.ResourcePoolService - $resourcePoolList = $resourcePool_helper.ResourcePool_GetResourcePoolTree($services,$vmobject.HostOrCluster) - $resourcePoolObj = $resourcePoolList | Where-Object { $_.resourcepooldata.name -eq $resourcePool } - if ($null -eq $resourcePoolObj) { - throw "No hostOrCluster found with Name: [$resourcePool]" + $vmObject.ResourcePool = Get-HVResourcePoolID $resourcePool_helper.ResourcePool_GetResourcePoolTree($services,$vmobject.HostOrCluster) + if ($null -eq $vmObject.ResourcePool) { + throw "No Resource Pool found with Name: [$resourcePool]" } - $vmObject.ResourcePool = $resourcePoolObj.id } return $vmObject } + +function Get-HVHostOrClusterID { +<# +.Synopsis + Recursive search for a Host or Cluster name within the results tree from HostOrCluster_GetHostOrClusterTree() and returns the ID + +.NOTES + HostOrCluster_GetHostOrClusterTree() returns a HostOrClusterTreeNode as below + + HostOrClusterTreeNode.container $true if this is a container + HostOrClusterTreeNode.treecontainer HostOrClusterTreeContainer + HostOrClusterTreeNode.treecontainer.name Container name + HostOrClusterTreeNode.treecontainer.path Path to this container + HostOrClusterTreeNode.treecontainer.type DATACENTER, FOLDER or OTHER + HostOrClusterTreeNode.treecontainer.children HostOrClusterTreeNode[] list of child nodes with potentially more child nodes + HostOrClusterTreeNode.info HostOrClusterInfo + HostOrClusterTreeNode.info.id Host or cluster ID + HostOrClusterTreeNode.info.cluster Is this a cluster + HostOrClusterTreeNode.info.name Host or cluster name + HostOrClusterTreeNode.info.path Path to host or cluster name + HostOrClusterTreeNode.info.virtualCenter + HostOrClusterTreeNode.info.datacenter + HostOrClusterTreeNode.info.vGPUTypes + HostOrClusterTreeNode.info.incompatibileReasons + + Author : Mark Elvers +#> + param( + [Parameter(Mandatory = $true)] + [VMware.Hv.HostOrClusterTreeNode]$hoctn + ) + if ($hoctn.container) { + foreach ($node in $hoctn.treeContainer.children) { + $id = Get-HVHostOrClusterID $node + if ($id -ne $null) { + return $id + } + } + } else { + if ($hoctn.info.path -eq $hostOrCluster -or $hoctn.info.name -eq $hostOrCluster) { + return $hoctn.info.id + } + } + return $null +} + +function Get-HVResourcePoolID { +<# +.Synopsis + Recursive search for a Resource Pool within the results tree from ResourcePool_GetResourcePoolTree() and returns the ID + +.NOTES + ResourcePool_GetResourcePoolTree() returns ResourcePoolInfo as below + + ResourcePoolInfo.id Resource pool ID + ResourcePoolInfo.resourcePoolData + ResourcePoolInfo.resourcePoolData.name Resource pool name + ResourcePoolInfo.resourcePoolData.path Resource pool path + ResourcePoolInfo.resourcePoolData.type HOST_OR_CLUSTER, RESOURCE_POOL or OTHER + ResourcePoolInfo.children ResourcePoolInfo[] list of child nodes with potentially further child nodes + + Author : Mark Elvers +#> + param( + [Parameter(Mandatory = $true)] + [VMware.Hv.ResourcePoolInfo]$rpi + ) + if ($rpi.resourcePoolData.path -eq $resourcePool -or $rpi.resourcePoolData.name -eq $resourcePool) { + return $rpi.id + } + foreach ($child in $rpi.children) { + $id = Get-HVResourcePoolID $child + if ($id -ne $null) { + return $id + } + } + return $null +} + +function Get-HVAccessGroupID { +<# +.Synopsis + Recursive search for an Acess Group within the results tree from AccessGroup_List() and returns the ID + +.NOTES + AccessGroup_List() returns AccessGroupInfo[] (a list of structures) + + Iterate through the list of structures + AccessGroupInfo.id Access Group ID + AccessGroupInfo.base + AccessGroupInfo.base.name Access Group name + AccessGroupInfo.base.description Access Group description + AccessGroupInfo.base.parent Access Group parent ID + AccessGroupInfo.data + AccessGroupInfo.data.permissions PermissionID[] + AccessGroupInfo.children AccessGroupInfo[] list of child nodes with potentially further child nodes + + I couldn't create a child node of a child node via the Horizon View Administrator GUI, but the this code allows that if it occurs + Furthermore, unless you are using the Root access group you must iterate over the children + + Root -\ + +- Access Group 1 + +- Access Group 2 + \- Access Group 3 + + Author : Mark Elvers +#> + param( + [Parameter(Mandatory = $true)] + [VMware.Hv.AccessGroupInfo[]]$agi + ) + foreach ($element in $agi) { + if ($element.base.name -eq $accessGroup) { + return $element.id + } + foreach ($child in $element.children) { + $id = Get-HVAccessGroupID $child + if ($id -ne $null) { + return $id + } + } + } + return $null +} + function Get-HVPoolStorageObject { param( - [Parameter(Mandatory = $false)] - [VMware.Hv.DesktopVirtualCenterStorageSettings]$StorageObject, - [Parameter(Mandatory = $true)] - [VMware.Hv.HostOrClusterId]$HostClusterID + [VMware.Hv.HostOrClusterId[]]$HostClusterIDs, + + [Parameter(Mandatory = $false)] + [VMware.Hv.DesktopVirtualCenterStorageSettings]$StorageObject ) + $datastoreList = $null if (!$storageObject) { + $datastore_helper = New-Object VMware.Hv.DatastoreService + foreach ($hostClusterID in $hostClusterIDs){ + $datastoreList += $datastore_helper.Datastore_ListDatastoresByHostOrCluster($services,$hostClusterID) + } $storageObject = New-Object VMware.Hv.DesktopVirtualCenterStorageSettings $storageAcceleratorList = @{ - 'useViewStorageAccelerator' = $false + 'useViewStorageAccelerator' = $useViewStorageAccelerator } $desktopViewStorageAcceleratorSettings = New-Object VMware.Hv.DesktopViewStorageAcceleratorSettings -Property $storageAcceleratorList $storageObject.viewStorageAcceleratorSettings = $desktopViewStorageAcceleratorSettings - $desktopSpaceReclamationSettings = New-Object VMware.Hv.DesktopSpaceReclamationSettings -Property @{ 'reclaimVmDiskSpace' = $false } + $desktopSpaceReclamationSettings = New-Object VMware.Hv.DesktopSpaceReclamationSettings -Property @{ 'reclaimVmDiskSpace' = $reclaimVmDiskSpace; 'reclamationThresholdGB' = $reclamationThresholdGB} $desktopPersistentDiskSettings = New-Object VMware.Hv.DesktopPersistentDiskSettings -Property @{ 'redirectWindowsProfile' = $false } $desktopNonPersistentDiskSettings = New-Object VMware.Hv.DesktopNonPersistentDiskSettings -Property @{ 'redirectDisposableFiles' = $false } + if ($LinkedClone) { + if ($blackoutTimes) { + $storageObject.viewStorageAcceleratorSettings.BlackoutTimes = $blackoutTimes + } + if ($useViewStorageAccelerator) { + $storageObject.viewStorageAcceleratorSettings.ViewComposerDiskTypes = $viewComposerDiskTypes + $storageObject.viewStorageAcceleratorSettings.RegenerateViewStorageAcceleratorDays = $regenerateViewStorageAcceleratorDays + } + $desktopPersistentDiskSettings.RedirectWindowsProfile = $redirectWindowsProfile + if ($redirectWindowsProfile) { + $desktopPersistentDiskSettings.UseSeparateDatastoresPersistentAndOSDisks = $useSeparateDatastoresPersistentAndOSDisks + $desktopPersistentDiskSettings.DiskSizeMB = $diskSizeMB + $desktopPersistentDiskSettings.DiskDriveLetter = $diskDriveLetter + } + if ($useSeparateDatastoresPersistentAndOSDisks) { + if ($persistentDiskStorageOvercommit -and ($persistentDiskDatastores.Length -ne $persistentDiskStorageOvercommit.Length) ) { + throw "Parameters persistentDiskDatastores length: [$persistentDiskDatastores.Length] and persistentDiskStorageOvercommit length: [$persistentDiskStorageOvercommit.Length] should be of same size" + } + $desktopPersistentDiskSettings.PersistentDiskDatastores = Get-HVDatastore -DatastoreInfoList $datastoreList -DatastoreNames $PersistentDiskDatastores -DsStorageOvercommit $persistentDiskStorageOvercommit + } + $desktopNonPersistentDiskSettings.RedirectDisposableFiles = $redirectDisposableFiles + $desktopNonPersistentDiskSettings.DiskSizeMB = $nonPersistentDiskSizeMB + $desktopNonPersistentDiskSettings.DiskDriveLetter = $nonPersistentDiskDriveLetter + } $desktopViewComposerStorageSettingsList = @{ - 'useSeparateDatastoresReplicaAndOSDisks' = $false; - 'useNativeSnapshots' = $false; + 'useNativeSnapshots' = $useNativeSnapshots; 'spaceReclamationSettings' = $desktopSpaceReclamationSettings; 'persistentDiskSettings' = $desktopPersistentDiskSettings; 'nonPersistentDiskSettings' = $desktopNonPersistentDiskSettings @@ -3522,17 +5106,13 @@ function Get-HVPoolStorageObject { } } if ($datastores) { - $datastore_helper = New-Object VMware.Hv.DatastoreService - $datastoreList = $datastore_helper.Datastore_ListDatastoresByHostOrCluster($services,$hostClusterID) - $datastoresSelected = @() - foreach ($ds in $datastores) { - $datastoresSelected += ($datastoreList | Where-Object { $_.datastoredata.name -eq $ds }).id + if ($StorageOvercommit -and ($datastores.Length -ne $StorageOvercommit.Length) ) { + throw "Parameters datastores length: [$datastores.Length] and StorageOvercommit length: [$StorageOvercommit.Length] should be of same size" } - foreach ($ds in $datastoresSelected) { - $myDatastores = New-Object VMware.Hv.DesktopVirtualCenterDatastoreSettings - $myDatastores.Datastore = $ds - $mydatastores.StorageOvercommit = 'UNBOUNDED' - $storageObject.Datastores += $myDatastores + $storageObject.Datastores = Get-HVDatastore -DatastoreInfoList $datastoreList -DatastoreNames $datastores -DsStorageOvercommit $StorageOvercommit + if ($useSeparateDatastoresReplicaAndOSDisks) { + $storageObject.ViewComposerStorageSettings.UseSeparateDatastoresReplicaAndOSDisks = $UseSeparateDatastoresReplicaAndOSDisks + $storageObject.ViewComposerStorageSettings.ReplicaDiskDatastore = ($datastoreList | Where-Object { ($_.datastoredata.name -eq $replicaDiskDatastore) -or ($_.datastoredata.path -eq $replicaDiskDatastore)}).id } } if ($storageObject.Datastores.Count -eq 0) { @@ -3542,6 +5122,41 @@ function Get-HVPoolStorageObject { return $storageObject } +function Get-HVDatastore { + param( + [Parameter(Mandatory = $true)] + [VMware.Hv.DatastoreInfo[]] + $DatastoreInfoList, + + [Parameter(Mandatory = $true)] + [string[]] + $DatastoreNames, + + [Parameter(Mandatory = $false)] + [string[]] + $DsStorageOvercommit + + ) + $datastoresSelected = @() + foreach ($ds in $datastoreNames) { + $datastoresSelected += ($datastoreInfoList | Where-Object { ($_.DatastoreData.Path -eq $ds) -or ($_.datastoredata.name -eq $ds) }).id + } + $Datastores = @() + $StorageOvercommitCnt = 0 + foreach ($ds in $datastoresSelected) { + $myDatastores = New-Object VMware.Hv.DesktopVirtualCenterDatastoreSettings + $myDatastores.Datastore = $ds + if (! $DsStorageOvercommit) { + $mydatastores.StorageOvercommit = 'UNBOUNDED' + } else { + $mydatastores.StorageOvercommit = $DsStorageOvercommit[$StorageOvercommitCnt] + } + $Datastores += $myDatastores + $StorageOvercommitCnt++ + } + return $Datastores +} + function Get-HVPoolNetworkSetting { param( [Parameter(Mandatory = $false)] @@ -3565,9 +5180,17 @@ function Get-HVPoolCustomizationSetting { # View Composer and Instant Clone Engine Active Directory container for QuickPrep and ClonePrep. This must be set for Instant Clone Engine or SVI sourced desktops. if ($InstantClone -or $LinkedClone) { $ad_domain_helper = New-Object VMware.Hv.ADDomainService - $adDomianId = ($ad_domain_helper.ADDomain_List($services) | Where-Object { $_.NetBiosName -eq $netBiosName } | Select-Object -Property id) - if ($null -eq $adDomianId) { - throw "No Domain found with netBiosName: [$netBiosName]" + $ADDomains = $ad_domain_helper.ADDomain_List($services) + if ($netBiosName) { + $adDomianId = ($ADDomains | Where-Object { $_.NetBiosName -eq $netBiosName } | Select-Object -Property id) + if ($null -eq $adDomianId) { + throw "No Domain found with netBiosName: [$netBiosName]" + } + } else { + $adDomianId = ($ADDomains[0] | Select-Object -Property id) + if ($null -eq $adDomianId) { + throw "No Domain configured in view administrator UI" + } } $ad_container_helper = New-Object VMware.Hv.AdContainerService $adContainerId = ($ad_container_helper.ADContainer_ListByDomain($services,$adDomianId.id) | Where-Object { $_.Rdn -eq $adContainer } | Select-Object -Property id).id @@ -3579,29 +5202,54 @@ function Get-HVPoolCustomizationSetting { if ($InstantClone) { $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CustomizationType = 'CLONE_PREP' $instantCloneEngineDomainAdministrator_helper = New-Object VMware.Hv.InstantCloneEngineDomainAdministratorService - $instantCloneEngineDomainAdministrator = ($instantCloneEngineDomainAdministrator_helper.InstantCloneEngineDomainAdministrator_List($services) | Where-Object { $_.namesData.dnsName -match $netBiosName }) + $insDomainAdministrators = $instantCloneEngineDomainAdministrator_helper.InstantCloneEngineDomainAdministrator_List($services) + $strFilterSet = @() + if (![string]::IsNullOrWhitespace($netBiosName)) { + $strFilterSet += '$_.namesData.dnsName -match $netBiosName' + } if (![string]::IsNullOrWhitespace($domainAdmin)) { - $instantCloneEngineDomainAdministrator = ($instantCloneEngineDomainAdministrator | Where-Object { $_.base.userName -eq $domainAdmin }).id - } else { + $strFilterSet += '$_.base.userName -eq $domainAdmin' + } + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $instantCloneEngineDomainAdministrator = $insDomainAdministrators | Where $scriptBlock + If ($null -ne $instantCloneEngineDomainAdministrator) { $instantCloneEngineDomainAdministrator = $instantCloneEngineDomainAdministrator[0].id + } elseif ($null -ne $insDomainAdministrators) { + $instantCloneEngineDomainAdministrator = $insDomainAdministrators[0].id } if ($null -eq $instantCloneEngineDomainAdministrator) { throw "No Instant Clone Engine Domain Administrator found with netBiosName: [$netBiosName]" } $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings = Get-CustomizationObject + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.ReusePreExistingAccounts = $reusePreExistingAccounts $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.InstantCloneEngineDomainAdministrator = $instantCloneEngineDomainAdministrator + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.powerOffScriptName = $powerOffScriptName + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.powerOffScriptParameters = $powerOffScriptParameters + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.postSynchronizationScriptName = $postSynchronizationScriptName + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CloneprepCustomizationSettings.postSynchronizationScriptParameters = $postSynchronizationScriptParameters } else { if ($LinkedClone) { $viewComposerDomainAdministrator_helper = New-Object VMware.Hv.ViewComposerDomainAdministratorService - $ViewComposerDomainAdministratorID = ($viewComposerDomainAdministrator_helper.ViewComposerDomainAdministrator_List($services,$vcID) | Where-Object { $_.base.domain -match $netBiosName }) + $lcDomainAdministrators = $viewComposerDomainAdministrator_helper.ViewComposerDomainAdministrator_List($services,$vcID) + $strFilterSet = @() + if (![string]::IsNullOrWhitespace($netBiosName)) { + $strFilterSet += '$_.base.domain -match $netBiosName' + } if (![string]::IsNullOrWhitespace($domainAdmin)) { - $ViewComposerDomainAdministratorID = ($ViewComposerDomainAdministratorID | Where-Object { $_.base.userName -ieq $domainAdmin }).id - } else { - $ViewComposerDomainAdministratorID = $ViewComposerDomainAdministratorID[0].id + $strFilterSet += '$_.base.userName -ieq $domainAdmin' + } + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $ViewComposerDomainAdministratorID = $lcDomainAdministrators | Where $scriptBlock + If ($null -ne $ViewComposerDomainAdministratorID) { + $ViewComposerDomainAdministratorID = $ViewComposerDomainAdministratorID[0].id + } elseif ($null -ne $lcDomainAdministrators) { + $ViewComposerDomainAdministratorID = $lcDomainAdministrators[0].id } if ($null -eq $ViewComposerDomainAdministratorID) { - throw "No Composer Domain Administrator found with netBiosName: [$netBiosName]" + throw "No Composer Domain Administrator found with netBiosName: [$netBiosName]" } if ($custType -eq 'SYS_PREP') { $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CustomizationType = 'SYS_PREP' @@ -3614,14 +5262,19 @@ function Get-HVPoolCustomizationSetting { throw "No Sysprep Customization Spec found with Name: [$sysPrepName]" } $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.SysprepCustomizationSettings.CustomizationSpec = $sysPrepIds[0].id - } elseif ($custType -eq 'QUICK_PREP') { + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.ReusePreExistingAccounts = $reusePreExistingAccounts + } elseIf ($custType -eq 'QUICK_PREP') { $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CustomizationType = 'QUICK_PREP' $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings = Get-CustomizationObject + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.powerOffScriptName = $powerOffScriptName + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.powerOffScriptParameters = $powerOffScriptParameters + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.postSynchronizationScriptName = $postSynchronizationScriptName + $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.QuickprepCustomizationSettings.postSynchronizationScriptParameters = $postSynchronizationScriptParameters } else { throw "The customization type: [$custType] is not supported for LinkedClone Pool" } $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.DomainAdministrator = $ViewComposerDomainAdministratorID - } elseif ($FullClone) { + } elseIf ($FullClone) { if ($custType -eq 'SYS_PREP') { $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CustomizationType = 'SYS_PREP' $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.SysprepCustomizationSettings = Get-CustomizationObject @@ -3632,7 +5285,7 @@ function Get-HVPoolCustomizationSetting { throw "No Sysprep Customization Spec found with Name: [$sysPrepName]" } $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.SysprepCustomizationSettings.CustomizationSpec = $sysPrepIds[0].id - } elseif ($custType -eq 'NONE') { + } elseIf ($custType -eq 'NONE') { $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.NoCustomizationSettings = Get-CustomizationObject $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.NoCustomizationSettings.DoNotPowerOnVMsAfterCreation = $false $desktopSpecObj.AutomatedDesktopSpec.CustomizationSettings.CustomizationType = "NONE" @@ -3649,7 +5302,7 @@ function Get-HVPoolCustomizationSetting { function Get-CustomizationObject { if ($InstantClone) { return New-Object VMware.Hv.DesktopCloneprepCustomizationSettings - } elseif ($LinkedClone) { + } elseIf ($LinkedClone) { if ($custType -eq 'QUICK_PREP') { return New-Object VMware.Hv.DesktopQuickPrepCustomizationSettings } else { @@ -3664,7 +5317,7 @@ function Get-CustomizationObject { } } -function Get-HVDesktopSpec { +function Get-DesktopSpec { param( [Parameter(Mandatory = $true)] @@ -3688,7 +5341,7 @@ function Get-HVDesktopSpec { if ($provisioningType -ne 'VIRTUAL_CENTER') { $desktop_spec_helper.getDataObject().AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.ViewComposerStorageSettings = $desktop_helper.getDesktopViewComposerStorageSettingsHelper().getDataObject() } - } elseif ($poolType -eq 'MANUAL') { + } elseIf ($poolType -eq 'MANUAL') { $desktop_spec_helper.getDataObject().ManualDesktopSpec.userAssignment = $desktop_helper.getDesktopUserAssignmentHelper().getDataObject() $desktop_spec_helper.getDataObject().ManualDesktopSpec.viewStorageAcceleratorSettings = $desktop_helper.getDesktopViewStorageAcceleratorSettingsHelper().getDataObject() $desktop_spec_helper.getDataObject().ManualDesktopSpec.virtualCenterManagedCommonSettings = $desktop_helper.getDesktopVirtualCenterManagedCommonSettingsHelper().getDataObject() @@ -3699,6 +5352,119 @@ function Get-HVDesktopSpec { } +function Test-HVPoolSpec { + param( + [Parameter(Mandatory = $true)] + $PoolObject + ) + if ($null -eq $PoolObject.type) { + Throw "Pool type is empty, need to be configured" + } + if ($null -eq $PoolObject.Base.Name) { + Throw "Pool name is empty, need to be configured" + } + if ($null -eq $PoolObject.Base.AccessGroup) { + Throw "AccessGroup of pool is empty, need to be configured" + } + if ($PoolObject.type -eq "AUTOMATED") { + if (! (($PoolObject.AutomatedDesktopSpec.UserAssignment.UserAssignment -eq "FLOATING") -or ($PoolObject.AutomatedDesktopSpec.UserAssignment.UserAssignment -eq "DEDICATED")) ) { + Throw "UserAssignment must be FLOATING or DEDICATED" + } + if ($PoolObject.AutomatedDesktopSpec.ProvisioningType -eq $null) { + Throw "Pool Provisioning type is empty, need to be configured" + } + $provisionTypeArray = @('VIRTUAL_CENTER', 'VIEW_COMPOSER', 'INSTANT_CLONE_ENGINE') + if (! ($provisionTypeArray -contains $PoolObject.AutomatedDesktopSpec.provisioningType)) { + Throw "ProvisioningType of pool is invalid" + } + if ($null -eq $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.EnableProvisioning) { + Throw "Whether to enable provisioning immediately or not, need to be configured" + } + if ($null -eq $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.StopProvisioningOnError) { + Throw "Whether to stop provisioning immediately or not on error, need to be configured" + } + if ($null -eq $PoolObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod) { + Throw "Determines how the VMs in the desktop are named, need to be configured" + } + if ($null -ne $PoolObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod) { + $namingMethodArray = @('PATTERN','SPECIFIED') + if (! ($namingMethodArray -contains $PoolObject.AutomatedDesktopSpec.VmNamingSpec.NamingMethod)) { + Throw "NamingMethod property must to be one of these SPECIFIED or PATTERN" + } + if (($null -eq $PoolObject.AutomatedDesktopSpec.VmNamingSpec.patternNamingSettings) -and ($null -eq $PoolObject.AutomatedDesktopSpec.VmNamingSpec.specificNamingSpec)) { + Throw "Naming pattern (or) Specified name settings need to be configured" + } + } + if ($null -eq $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.UseVSan) { + Throw "Must specify whether to use virtual SAN or not" + } + $jsonTemplate = $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Template + $jsonParentVm = $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ParentVm + $jsonSnapshot = $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.Snapshot + $jsonVmFolder = $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.VmFolder + $jsonHostOrCluster = $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.HostOrCluster + $ResourcePool = $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.virtualCenterProvisioningData.ResourcePool + if (! (($null -ne $jsonTemplate) -or (($null -ne $jsonParentVm) -and ($null -ne $jsonSnapshot) ))) { + Throw "Must specify Template or (ParentVm and Snapshot) names" + } + if ($null -eq $jsonVmFolder) { + Throw "Must specify VM folder to deploy the VMs" + } + if ($null -eq $jsonHostOrCluster) { + Throw "Must specify HostOrCluster to deploy the VMs" + } + if ($null -eq $resourcePool) { + Throw "Must specify Resource pool to deploy the VMs" + } + if ($null -eq $PoolObject.AutomatedDesktopSpec.VirtualCenterProvisioningSettings.VirtualCenterStorageSettings.Datastores) { + Throw "Must specify datastores names" + } + if ($null -eq $PoolObject.AutomatedDesktopSpec.VirtualCenterManagedCommonSettings.transparentPageSharingScope) { + Throw "Must specify transparent page sharing scope" + } + $jsonCustomizationType = $PoolObject.AutomatedDesktopSpec.CustomizationSettings.CustomizationType + switch ($jsonCustomizationType) { + "NONE" { + if ($null -eq $PoolObject.AutomatedDesktopSpec.CustomizationSettings.noCustomizationSettings) { + Throw "Specify noCustomization Settings" + } + } + "QUICK_PREP" { + if ($null -eq $PoolObject.AutomatedDesktopSpec.CustomizationSettings.quickprepCustomizationSettings) { + Throw "Specify quickPrep customizationSettings" + } + } + "SYS_PREP" { + if ($null -eq $PoolObject.AutomatedDesktopSpec.CustomizationSettings.sysprepCustomizationSettings) { + Throw "Specify sysPrep customizationSettings" + } + } + "CLONE_PREP" { + if ($null -eq $PoolObject.AutomatedDesktopSpec.CustomizationSettings.cloneprepCustomizationSettings) { + Throw "Specify clonePrep customizationSettings" + } + } + } + } elseIf ($PoolObject.Type -eq "MANUAL") { + $jsonUserAssignment = $PoolObject.ManualDesktopSpec.UserAssignment.UserAssignment + if (! (($jsonUserAssignment -eq "FLOATING") -or ($jsonUserAssignment -eq "DEDICATED")) ) { + Throw "UserAssignment must be FLOATING or DEDICATED" + } + $jsonSource = @('VIRTUAL_CENTER','UNMANAGED') + if (! ($jsonSource -contains $PoolObject.ManualDesktopSpec.Source)) { + Throw "The Source of machines must be VIRTUAL_CENTER or UNMANAGED" + } + if ($null -eq $PoolObject.ManualDesktopSpec.Machines) { + Throw "Specify list of virtual machines to be added to this pool" + } + } + elseIf ($PoolObject.type -eq "RDS") { + if ($null -eq $PoolObject.RdsDesktopSpec.Farm) { + Throw "Specify farm needed to create RDS desktop" + } + } +} + function Remove-HVFarm { <# .SYNOPSIS @@ -3714,29 +5480,32 @@ function Remove-HVFarm { Object(s) of the farm to be deleted. Object(s) should be of type FarmSummaryView/FarmInfo. .PARAMETER HvServer - Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. + Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE - Remove-HVFarm -FarmName 'Farm-01' -HvServer $hvServer + Remove-HVFarm -FarmName 'Farm-01' -HvServer $hvServer -Confirm:$false + Delete a given farm. For an automated farm, all the RDS Server VMs are deleted from disk whereas for a manual farm only the RDS Server associations are removed. .EXAMPLE $farm_array | Remove-HVFarm -HvServer $hvServer + Deletes a given Farm object(s). For an automated farm, all the RDS Server VMs are deleted from disk whereas for a manual farm only the RDS Server associations are removed. .EXAMPLE - $farm1 = Get-HVFarm -FarmName 'Farm-01' - Remove-HVFarm -Farm $farm1 + C:\PS>$farm1 = Get-HVFarm -FarmName 'Farm-01' + C:\PS>Remove-HVFarm -Farm $farm1 + Deletes a given Farm object. For an automated farm, all the RDS Server VMs are deleted from disk whereas for a manual farm only the RDS Server associations are removed. .OUTPUTS None .NOTES - Author : Ankit Gupta. - Author email : guptaa@vmware.com - Version : 1.0 + Author : praveen mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -3765,26 +5534,27 @@ function Remove-HVFarm { } } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys $farmList = @() if ($farmName) { try { - $farmSpecObj = Get-HVFarm -farmName $farmName -hvServer $hvServer + $farmSpecObj = Get-HVFarm -farmName $farmName -hvServer $hvServer -SuppressInfo $true } catch { Write-Error "Make sure Get-HVFarm advanced function is loaded, $_" break } if ($farmSpecObj) { foreach ($farmObj in $farmSpecObj) { - $farmList += $farmObj.id + $farmList += @{"id" = $farmObj.id; "Name" = $farmObj.data.name} } } else { Write-Error "Unable to retrieve FarmSummaryView with given farmName [$farmName]" break } - } elseif ($PSCmdlet.MyInvocation.ExpectingInput) { + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Farm) { foreach ($item in $farm) { - if ($item.GetType().name -eq 'FarmInfo' -or $item.GetType().name -eq 'FarmSummaryView') { - $farmList += $item.id + if (($item.GetType().name -eq 'FarmInfo') -or ($item.GetType().name -eq 'FarmSummaryView')) { + $farmList += @{"id" = $item.id; "Name" = $item.data.name} } else { Write-Error "In pipeline did not get object of expected type FarmSummaryView/FarmInfo" @@ -3795,10 +5565,11 @@ function Remove-HVFarm { } $farm_service_helper = New-Object VMware.Hv.FarmService foreach ($item in $farmList) { - $farm_service_helper.Farm_Delete($services, $item) + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($item.Name)) { + $farm_service_helper.Farm_Delete($services, $item.id) + } + Write-Host "Farm Deleted: " $item.Name } - Write-Host "Farm Deleted" - } end { [System.gc]::collect() @@ -3830,13 +5601,16 @@ function Remove-HVPool { Logs off a session forcibly to virtual machine(s). This operation will also log off a locked session. .EXAMPLE - Remove-HVPool -HvServer $hvServer -PoolName 'FullClone' -DeleteFromDisk + Remove-HVPool -HvServer $hvServer -PoolName 'FullClone' -DeleteFromDisk -Confirm:$false + Deletes pool from disk with given parameters PoolName etc. .EXAMPLE $pool_array | Remove-HVPool -HvServer $hvServer -DeleteFromDisk + Deletes specified pool from disk .EXAMPLE Remove-HVPool -Pool $pool1 + Deletes specified pool and VM(s) associations are removed from view Manager .OUTPUTS None @@ -3844,11 +5618,11 @@ function Remove-HVPool { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -3857,7 +5631,7 @@ function Remove-HVPool { ConfirmImpact = 'High' )] param( - [Parameter(Mandatory = $false,ParameterSetName = 'option')] + [Parameter(Mandatory = $true,ParameterSetName = 'option')] [string] $poolName, # PoolObject @@ -3883,26 +5657,30 @@ function Remove-HVPool { } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys $poolList = @() if ($poolName) { try { - $myPools = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer + $myPools = Get-HVPoolSummary -poolName $poolName -suppressInfo $true -hvServer $hvServer } catch { Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" break } if ($myPools) { foreach ($poolObj in $myPools) { - $poolList += $poolObj.id + $poolList += @{id = $poolObj.id; name = $poolObj.desktopSummaryData.name} } } else { Write-Error "No desktopsummarydata found with pool name: [$pool]" break } - } elseif ($PSCmdlet.MyInvocation.ExpectingInput) { + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Pool) { foreach ($item in $pool) { - if (($item.GetType().name -eq 'DesktopInfo') -or ($item.GetType().name -eq 'DesktopSummaryView')) { - $poolList += $item.id + if ($item.GetType().name -eq 'DesktopSummaryView') { + $poolList += @{id = $item.id; name = $item.desktopSummaryData.name} + } + elseif ($item.GetType().name -eq 'DesktopInfo') { + $poolList += @{id = $item.id; name = $item.base.name} } else { Write-Error "In pipeline did not get object of expected type DesktopSummaryView/DesktopInfo" @@ -3917,9 +5695,8 @@ function Remove-HVPool { foreach ($item in $poolList) { if ($terminateSession) { #Terminate session - $queryResults = Get-HVQueryResults MachineSummaryView (Get-HVQueryFilter base.desktop -eq $item) + $queryResults = Get-HVQueryResult MachineSummaryView (Get-HVQueryFilter base.desktop -eq $item.id) $sessions += $queryResults.base.session - if ($null -ne $sessions) { $session_service_helper = New-Object VMware.Hv.SessionService try { @@ -3932,8 +5709,10 @@ function Remove-HVPool { Write-Host "No session found." } } - Write-Host "Deleting Pool" - $desktop_service_helper.Desktop_Delete($services,$item,$deleteSpec) + Write-Host "Deleting Pool: " $item.Name + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($item.Name)) { + $desktop_service_helper.Desktop_Delete($services,$item.id,$deleteSpec) + } } } @@ -3978,34 +5757,39 @@ function Set-HVFarm { Path of the JSON specification file containing key/value pair. .PARAMETER HvServer - Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. + Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE - Set-HVFarm -FarmName 'Farm-o1' -Spec 'C:\Edit-HVFarm\ManualEditFarm.json' + Set-HVFarm -FarmName 'Farm-01' -Spec 'C:\Edit-HVFarm\ManualEditFarm.json' -Confirm:$false + Updates farm configuration by using json file .EXAMPLE - Set-HVFarm -FarmName 'Farm-o1' -Key 'base.description' -Value 'updated description' + Set-HVFarm -FarmName 'Farm-01' -Key 'base.description' -Value 'updated description' + Updates farm configuration with given parameters key and value .EXAMPLE $farm_array | Set-HVFarm -Key 'base.description' -Value 'updated description' + Updates farm(s) configuration with given parameters key and value .EXAMPLE Set-HVFarm -farm 'Farm2' -Start + Enables provisioning to specified farm .EXAMPLE Set-HVFarm -farm 'Farm2' -Enable + Enables specified farm .OUTPUTS None .NOTES - Author : Ankit Gupta. - Author email : guptaa@vmware.com - Version : 1.0 + Author : praveen mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -4054,12 +5838,13 @@ function Set-HVFarm { } process { - $farmList = @() + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys + $farmList = @{} if ($farmName) { try { - $farmSpecObj = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer + $farmSpecObj = Get-HVFarmSummary -farmName $farmName -hvServer $hvServer -suppressInfo $true } catch { - Write-Error "Make sure Get-HVFarm advanced function is loaded, $_" + Write-Error "Make sure Get-HVFarmSummary advanced function is loaded, $_" break } if ($farmSpecObj) { @@ -4068,7 +5853,7 @@ function Set-HVFarm { Write-Error "Start/Stop operation is not supported for farm with name : [$farmObj.Data.Name]" return } - $farmList += $farmObj.id + $farmList.add($farmObj.id, $farmObj.data.name) } } else { Write-Error "Unable to retrieve FarmSummaryView with given farmName [$farmName]" @@ -4081,14 +5866,14 @@ function Set-HVFarm { Write-Error "Start/Stop operation is not supported for farm with name : [$item.Data.Name]" return } - $farmList += $item.id + $farmList.add($item.id, $item.data.name) } elseif ($item.GetType().name -eq 'FarmInfo') { if (($Start -or $Stop) -and ("AUTOMATED" -ne $item.Type)) { Write-Error "Start/Stop operation is not supported for farm with name : [$item.Data.Name]" return } - $farmList += $item.id + $farmList.add($item.id, $item.data.name) } else { Write-Error "In pipeline did not get object of expected type FarmSummaryView/FarmInfo" @@ -4125,8 +5910,11 @@ function Set-HVFarm { -value $false } $farm_service_helper = New-Object VMware.Hv.FarmService - foreach ($item in $farmList) { - $farm_service_helper.Farm_Update($services,$item,$updates) + foreach ($item in $farmList.Keys) { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($farmList.$item)) { + $farm_service_helper.Farm_Update($services,$item,$updates) + } + Write-Host "Update successful for farm: " $farmList.$item } } @@ -4175,22 +5963,28 @@ function Set-HVPool { Path of the JSON specification file containing key/value pair. .EXAMPLE - Set-HVPool -PoolName 'ManualPool' -Spec 'C:\Edit-HVPool\EditPool.json' + Set-HVPool -PoolName 'ManualPool' -Spec 'C:\Edit-HVPool\EditPool.json' -Confirm:$false + Updates pool configuration by using json file .EXAMPLE Set-HVPool -PoolName 'RDSPool' -Key 'base.description' -Value 'update description' + Updates pool configuration with given parameters key and value .Example Set-HVPool -PoolName 'LnkClone' -Disable + Disables specified pool .Example Set-HVPool -PoolName 'LnkClone' -Enable + Enables specified pool .Example Set-HVPool -PoolName 'LnkClone' -Start + Enables provisioning to specified pool .Example Set-HVPool -PoolName 'LnkClone' -Stop + Disables provisioning to specified pool .OUTPUTS None @@ -4198,11 +5992,12 @@ function Set-HVPool { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.2 + Updated : Mark Elvers ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -4240,6 +6035,20 @@ function Set-HVPool { [Parameter(Mandatory = $false)] [string]$Spec, + [Parameter(Mandatory = $false)] + [string] + $globalEntitlement, + + [Parameter(Mandatory = $false)] + [string] + $ResourcePool, + + [Parameter(Mandatory = $false)] + [boolean]$allowUsersToChooseProtocol, + + [Parameter(Mandatory = $false)] + [boolean]$enableHTMLAccess, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -4253,38 +6062,42 @@ function Set-HVPool { } process { - $poolList = @() + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys + $poolList = @{} if ($poolName) { try { - $desktopPools = Get-HVPoolSummary -poolName $poolName -hvServer $hvServer + $desktopPools = Get-HVPoolSummary -poolName $poolName -suppressInfo $true -hvServer $hvServer } catch { Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" break } if ($desktopPools) { foreach ($desktopObj in $desktopPools) { - if (($Start -or $Stop) -and ("AUTOMATED" -ne $item.DesktopSummaryData.Type)) { - Write-Error "Start/Stop operation is not supported for Poll with name : [$item.DesktopSummaryData.Name]" + if (($Start -or $Stop) -and ("AUTOMATED" -ne $desktopObj.DesktopSummaryData.Type)) { + Write-Error "Start/Stop operation is not supported for Pool with name : [$desktopObj.DesktopSummaryData.Name]" return } - $poolList += $desktopObj.id + $poolList.add($desktopObj.id, $desktopObj.DesktopSummaryData.Name) } + } else { + Write-Error "No desktopsummarydata found with pool name: [$poolName]" + break } - } elseif ($PSCmdlet.MyInvocation.ExpectingInput) { + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Pool) { foreach ($item in $pool) { if ($item.GetType().name -eq 'DesktopInfo') { if (($Start -or $Stop) -and ("AUTOMATED" -ne $item.Type)) { Write-Error "Start/Stop operation is not supported for Pool with name : [$item.Base.Name]" return } - $poolList += $item.id + $poolList.add($item.id, $item.Base.Name) } elseif ($item.GetType().name -eq 'DesktopSummaryView') { if (($Start -or $Stop) -and ("AUTOMATED" -ne $item.DesktopSummaryData.Type)) { Write-Error "Start/Stop operation is not supported for Poll with name : [$item.DesktopSummaryData.Name]" return } - $poolList += $item.id + $poolList.add($item.id, $item.DesktopSummaryData.Name) } else { Write-Error "In pipeline did not get object of expected type DesktopSummaryView/DesktopInfo" @@ -4294,9 +6107,9 @@ function Set-HVPool { } } $updates = @() - if ($key -and $value) { + if ($PSBoundParameters.ContainsKey("key") -and $PSBoundParameters.ContainsKey("value")) { $updates += Get-MapEntry -key $key -value $value - } elseif ($key -or $value) { + } elseif ($PSBoundParameters.ContainsKey("key") -or $PSBoundParameters.ContainsKey("value")) { Write-Error "Both key:[$key] and value:[$value] needs to be specified" } if ($spec) { @@ -4324,9 +6137,51 @@ function Set-HVPool { $updates += Get-MapEntry -key 'automatedDesktopData.virtualCenterProvisioningSettings.enableProvisioning' ` -value $false } + + if ($PSBoundParameters.ContainsKey("allowUsersToChooseProtocol")) { + $updates += Get-MapEntry -key 'desktopSettings.displayProtocolSettings.allowUsersToChooseProtocol' -value $allowUsersToChooseProtocol + } + + if ($PSBoundParameters.ContainsKey("enableHTMLAccess")) { + $updates += Get-MapEntry -key 'desktopSettings.displayProtocolSettings.enableHTMLAccess' -value $enableHTMLAccess + } + + if ($PSBoundParameters.ContainsKey("ResourcePool")) { + foreach ($item in $poolList.Keys) { + $pool = Get-HVPool -PoolName $poolList.$item + $ResourcePool_service_helper = New-Object VMware.Hv.ResourcePoolService + $ResourcePoolID = Get-HVResourcePoolID $ResourcePool_service_helper.ResourcePool_GetResourcePoolTree($services, $pool.AutomatedDesktopData.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.HostOrCluster) + $updates += Get-MapEntry -key 'automatedDesktopData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.resourcePool' -value $ResourcePoolID + } + } + + $info = $services.PodFederation.PodFederation_get() + if ($globalEntitlement -and ("ENABLED" -eq $info.localPodStatus.status)) { + $QueryFilterEquals = New-Object VMware.Hv.QueryFilterEquals + $QueryFilterEquals.memberName = 'base.displayName' + $QueryFilterEquals.value = $globalEntitlement + $defn = New-Object VMware.Hv.QueryDefinition + $defn.queryEntityType = 'GlobalEntitlementSummaryView' + $defn.Filter = $QueryFilterEquals + $query_service_helper = New-Object VMware.Hv.QueryServiceService + try { + $queryResults = $query_service_helper.QueryService_Query($services,$defn) + $globalEntitlementid = $queryResults.Results.id + if ($globalEntitlementid.length -eq 1) { + $updates += Get-MapEntry -key 'globalEntitlementData.globalEntitlement' -value $globalEntitlementid + } + } + catch { + Write-Host "GlobalEntitlement " $_ + } + } + $desktop_helper = New-Object VMware.Hv.DesktopService - foreach ($item in $poolList) { - $desktop_helper.Desktop_Update($services,$item,$updates) + foreach ($item in $poolList.Keys) { + Write-Host "Updating the Pool: " $poolList.$item + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($poolList.$item)) { + $desktop_helper.Desktop_Update($services,$item,$updates) + } } } @@ -4338,10 +6193,10 @@ function Set-HVPool { function Start-HVFarm { <# .SYNOPSIS - Perform maintenance tasks on the farm(s). + Performs maintenance tasks on the farm(s). .DESCRIPTION - This function is used to perform maintenance tasks like enable/disable, start/stop and recompose the farm. + This function is used to perform maintenance tasks like enable/disable, start/stop and recompose the farm. This function is also used for scheduling maintenance operation on instant-clone farm(s). .PARAMETER Farm Name/Object(s) of the farm. Object(s) should be of type FarmSummaryView/FarmInfo. @@ -4349,16 +6204,23 @@ function Start-HVFarm { .PARAMETER Recompose Switch for recompose operation. Requests a recompose of RDS Servers in the specified 'AUTOMATED' farm. This marks the RDS Servers for recompose, which is performed asynchronously. +.PARAMETER ScheduleMaintenance + Switch for ScheduleMaintenance operation. Requests for scheduling maintenance operation on RDS Servers in the specified Instant clone farm. This marks the RDS Servers for scheduled maintenance, which is performed according to the schedule. + +.PARAMETER CancelMaintenance + Switch for cancelling maintenance operation. Requests for cancelling a scheduled maintenance operation on the specified Instant clone farm. This stops further maintenance operation on the given farm. + .PARAMETER StartTime - Specifies when to start the operation. If unset, the operation will begin immediately. + Specifies when to start the recompose/ScheduleMaintenance operation. If unset, the recompose operation will begin immediately. + For IMMEDIATE maintenance if unset, maintenance will begin immediately. For RECURRING maintenance if unset, will be calculated based on recurring maintenance configuration. If in the past, maintenance will begin immediately. .PARAMETER LogoffSetting Determines when to perform the operation on machines which have an active session. This property will be one of: - "FORCE_LOGOFF" - Users will be forced to log off when the system is ready to operate on their RDS Servers. Before being forcibly logged off, users may have a grace period in which to save their work (Global Settings). + "FORCE_LOGOFF" - Users will be forced to log off when the system is ready to operate on their RDS Servers. Before being forcibly logged off, users may have a grace period in which to save their work (Global Settings). This is the default value. "WAIT_FOR_LOGOFF" - Wait for connected users to disconnect before the task starts. The operation starts immediately on RDS Servers without active sessions. .PARAMETER StopOnFirstError - Indicates that the operation should stop on first error. + Indicates that the operation should stop on first error. Defaults to true. .PARAMETER Servers The RDS Server(s) id to recompose. Provide a comma separated list for multiple RDSServerIds. @@ -4372,27 +6234,65 @@ function Start-HVFarm { .PARAMETER Vcenter Virtual Center server-address (IP or FQDN) of the given farm. This should be same as provided to the Connection Server while adding the vCenter server. +.PARAMETER MaintenanceMode + The mode of schedule maintenance for Instant Clone Farm. This property will be one of: + "IMMEDIATE" - All server VMs will be refreshed once, immediately or at user scheduled time. + "RECURRING" - All server VMs will be periodically refreshed based on MaintenancePeriod and MaintenanceStartTime. + +.PARAMETER MaintenanceStartTime + Configured start time for the recurring maintenance. This property must be in the form hh:mm in 24 hours format. + +.PARAMETER MaintenancePeriod + This represents the frequency at which to perform recurring maintenance. This property will be one of: + "DAILY" - Daily recurring maintenance + "WEEKLY" - Weekly recurring maintenance + "MONTHLY" - Monthly recurring maintenance + +.PARAMETER StartInt + Start index for weekly or monthly maintenance. Weekly: 1-7 (Sun-Sat), Monthly: 1-31. + This property is required if maintenancePeriod is set to "WEEKLY"or "MONTHLY". + This property has values 1-7 for maintenancePeriod "WEEKLY". + This property has values 1-31 for maintenancePeriod "MONTHLY". + +.PARAMETER EveryInt + How frequently to repeat maintenance, expressed as a multiple of the maintenance period. e.g. Every 2 weeks. + This property has a default value of 1. This property has values 1-100. + .PARAMETER HvServer - Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered inplace of hvServer. + Reference to Horizon View Server to query the data from. If the value is not passed or null then first element from global:DefaultHVServers would be considered in-place of hvServer. .EXAMPLE - Start-HVFarm -Recompose -Farm 'Farm-01' -LogoffSetting FORCE_LOGOFF -ParentVM 'View-Agent-Win8' -SnapshotVM 'Snap_USB' + Start-HVFarm -Recompose -Farm 'Farm-01' -LogoffSetting FORCE_LOGOFF -ParentVM 'View-Agent-Win8' -SnapshotVM 'Snap_USB' -Confirm:$false + Requests a recompose of RDS Servers in the specified automated farm .EXAMPLE - $myTime = Get-Date '10/03/2016 12:30:00' - Start-HVFarm -Farm 'Farm-01' -Recompose -LogoffSetting 'FORCE_LOGOFF' -ParentVM 'ParentVM' -SnapshotVM 'SnapshotVM' -StartTime $myTime + C:\PS>$myTime = Get-Date '10/03/2016 12:30:00' + C:\PS>Start-HVFarm -Farm 'Farm-01' -Recompose -LogoffSetting 'FORCE_LOGOFF' -ParentVM 'ParentVM' -SnapshotVM 'SnapshotVM' -StartTime $myTime + Requests a recompose task for automated farm in specified time + +.EXAMPLE + Start-HVFarm -Farm 'ICFarm-01' -ScheduleMaintenance -MaintenanceMode IMMEDIATE + Requests a ScheduleMaintenance task for instant-clone farm. Schedules an IMMEDIATE maintenance. + +.EXAMPLE + Start-HVFarm -ScheduleMaintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING -MaintenancePeriod WEEKLY -MaintenanceStartTime '11:30' -StartInt 6 -EveryInt 1 -ParentVM 'vm-rdsh-ic' -SnapshotVM 'Snap_Updated' + Requests a ScheduleMaintenance task for instant-clone farm. Schedules a recurring weekly maintenace every Saturday night at 23:30 and updates the parentVM and snapshot. + +.EXAMPLE + Start-HVFarm -CancelMaintenance -Farm 'ICFarm-01' -MaintenanceMode RECURRING + Requests a CancelMaintenance task for instant-clone farm. Cancels recurring maintenance. .OUTPUTS None .NOTES - Author : Ankit Gupta. - Author email : guptaa@vmware.com - Version : 1.0 + Author : praveen mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -4407,28 +6307,61 @@ function Start-HVFarm { [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] [switch]$Recompose, + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [switch]$ScheduleMaintenance, + + [Parameter(Mandatory = $false,ParameterSetName = 'CANCELMAINTENANCE')] + [switch]$CancelMaintenance, + [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [System.DateTime]$StartTime, [Parameter(Mandatory = $true,ParameterSetName = 'RECOMPOSE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [ValidateSet('FORCE_LOGOFF','WAIT_FOR_LOGOFF')] - [string]$LogoffSetting, + [string]$LogoffSetting = 'FORCE_LOGOFF', [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [boolean]$StopOnFirstError = $true, [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] [string []]$Servers, [Parameter(Mandatory = $true,ParameterSetName = 'RECOMPOSE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [string]$ParentVM, [Parameter(Mandatory = $true,ParameterSetName = 'RECOMPOSE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [string]$SnapshotVM, [Parameter(Mandatory = $false,ParameterSetName = 'RECOMPOSE')] + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] [string]$Vcenter, + [Parameter(Mandatory = $true,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [Parameter(Mandatory = $true,ParameterSetName = 'CANCELMAINTENANCE')] + [ValidateSet('IMMEDIATE','RECURRING')] + [string]$MaintenanceMode, + + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [ValidatePattern('^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$')] + [string]$MaintenanceStartTime, + + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [ValidateSet('DAILY','WEEKLY','MONTHLY')] + [string]$MaintenancePeriod, + + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [ValidateRange(1, 31)] + [int]$StartInt, + + [Parameter(Mandatory = $false,ParameterSetName = 'SCHEDULEMAINTENANCE')] + [ValidateRange(1, 100)] + [int]$EveryInt = 1, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -4442,6 +6375,7 @@ function Start-HVFarm { } process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys $farmList = @{} $farmType = @{} $farmSource = @{} @@ -4451,15 +6385,17 @@ function Start-HVFarm { $id = $farm.id $name = $farm.data.name $type = $farm.type + $source = $farm.source } elseif ($farm.GetType().name -eq 'FarmSummaryView') { $id = $farm.id $name = $farm.data.name $type = $farm.data.type + $source = $farm.data.source } elseif ($farm.GetType().name -eq 'String') { try { - $farmSpecObj = Get-HVFarm -farmName $farm -hvServer $hvServer + $farmSpecObj = Get-HVFarm -farmName $farm -hvServer $hvServer -SuppressInfo $true } catch { Write-Error "Make sure Get-HVFarm advanced function is loaded, $_" break @@ -4467,7 +6403,8 @@ function Start-HVFarm { if ($farmSpecObj) { $id = $farmSpecObj.id $name = $farmSpecObj.data.name - $type = $farmSpecObj.data.type + $type = $farmSpecObj.type + $source = $farmSpecObj.source } else { Write-Error "Unable to retrieve FarmSummaryView with given farmName [$farm]" break @@ -4476,7 +6413,7 @@ function Start-HVFarm { Write-Error "In pipeline did not get object of expected type FarmSummaryView/FarmInfo" break } - if ($type -eq 'AUTOMATED') { + if (!$source) { $source = 'VIEW_COMPOSER' } $farmList.Add($id,$name) @@ -4519,12 +6456,73 @@ function Start-HVFarm { $updates = @() $updates += Get-MapEntry -key 'automatedFarmData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.parentVm' -value $spec.ParentVM $updates += Get-MapEntry -key 'automatedFarmData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.snapshot' -value $spec.Snapshot - $farm_service_helper.Farm_Update($services,$item,$updates) - - $farm_service_helper.Farm_Recompose($services,$item,$spec) + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($farmList.$item)) { + $farm_service_helper.Farm_Update($services,$item,$updates) + $farm_service_helper.Farm_Recompose($services,$item,$spec) + } + Write-Host "Performed recompose task on farm: " $farmList.$item + } + } + 'SCHEDULEMAINTENANCE' { + if ($farmSource.$item -ne 'INSTANT_CLONE_ENGINE') { + Write-Error "SCHEDULEMAINTENANCE operation is not supported for farm with name [$farmList.$item]. It is only supported for instant-clone farms." + break + } else { + $spec = New-Object VMware.Hv.FarmMaintenanceSpec + $spec.MaintenanceMode = $MaintenanceMode + if ($startTime) { + $spec.ScheduledTime = $StartTime + } + $spec.LogoffSetting = $LogoffSetting + $spec.StopOnFirstError = $StopOnFirstError + if ($MaintenanceMode -eq "RECURRING") { + $spec.RecurringMaintenanceSettings = New-Object VMware.Hv.FarmRecurringMaintenanceSettings + $spec.RecurringMaintenanceSettings.MaintenancePeriod = $MaintenancePeriod + $spec.RecurringMaintenanceSettings.EveryInt = $EveryInt + if (!$MaintenanceStartTime) { + Write-Error "MaintenanceStartTime must be defined for MaintenanceMode = RECURRING." + break; + } else { + $spec.RecurringMaintenanceSettings.StartTime = $MaintenanceStartTime + } + if ($MaintenancePeriod -ne 'DAILY') { + if (!$StartInt) { + Write-Error "StartInt must be defined for MaintenancePeriod WEEKLY or MONTHLY." + break; + } else { + $spec.RecurringMaintenanceSettings.StartInt = $StartInt + } + } + } + #image settings are specified + if ($ParentVM -and $SnapshotVM) { + $spec.ImageMaintenanceSettings = New-Object VMware.Hv.FarmImageMaintenanceSettings + $vcId = Get-VcenterID -services $services -vCenter $Vcenter + if ($null -eq $vcId) { + Write-Error "VCenter is required if you specify ParentVM name." + break + } + try { + $spec.ImageMaintenanceSettings = Set-HVFarmSpec -vcId $vcId -spec $spec.ImageMaintenanceSettings + } catch { + Write-Error "SCHEDULEMAINTENANCE task failed with error: $_" + break + } + } + # call scheduleMaintenance service on farm + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($farmList.$item)) { + $farm_service_helper.Farm_ScheduleMaintenance($services, $item, $spec) + Write-Host "Performed SCHEDULEMAINTENANCE task on farm: " $farmList.$item + } + } + } + 'CANCELMAINTENANCE' { + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($farmList.$item)) { + $farm_service_helper.Farm_CancelScheduleMaintenance($services, $item, $MaintenanceMode) + Write-Host "Performed CANCELMAINTENANCE task on farm: " $farmList.$item + } } } - } return } } @@ -4645,19 +6643,24 @@ function Start-HVPool { .EXAMPLE Start-HVPool -Recompose -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF -ParentVM 'View-Agent-Win8' -SnapshotVM 'Snap_USB' + Requests a recompose of machines in the specified pool .EXAMPLE - Start-HVPool -Refresh -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF + Start-HVPool -Refresh -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF -Confirm:$false + Requests a refresh of machines in the specified pool .EXAMPLE - $myTime = Get-Date '10/03/2016 12:30:00' - Start-HVPool -Rebalance -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF -StartTime $myTime + C:\PS>$myTime = Get-Date '10/03/2016 12:30:00' + C:\PS>Start-HVPool -Rebalance -Pool 'LCPool3' -LogoffSetting FORCE_LOGOFF -StartTime $myTime + Requests a rebalance of machines in a pool with specified time .EXAMPLE Start-HVPool -SchedulePushImage -Pool 'InstantPool' -LogoffSetting FORCE_LOGOFF -ParentVM 'InsParentVM' -SnapshotVM 'InsSnapshotVM' + Requests an update of push image operation on the specified Instant Clone Engine sourced pool .EXAMPLE Start-HVPool -CancelPushImage -Pool 'InstantPool' + Requests a cancellation of the current scheduled push image operation on the specified Instant Clone Engine sourced pool .OUTPUTS None @@ -4665,11 +6668,11 @@ function Start-HVPool { .NOTES Author : Praveen Mathamsetty. Author email : pmathamsetty@vmware.com - Version : 1.0 + Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -4747,7 +6750,7 @@ function Start-HVPool { } process { - + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys $poolList = @{} $poolType = @{} $poolSource = @{} @@ -4765,7 +6768,7 @@ function Start-HVPool { $type = $item.desktopsummarydata.type } elseif ($item.GetType().name -eq 'String') { try { - $poolObj = Get-HVPoolSummary -poolName $item -hvServer $hvServer + $poolObj = Get-HVPoolSummary -poolName $item -suppressInfo $true -hvServer $hvServer } catch { Write-Error "Make sure Get-HVPoolSummary advanced function is loaded, $_" break @@ -4800,14 +6803,20 @@ function Start-HVPool { $spec = Get-HVTaskSpec -Source $poolSource.$item -poolName $poolList.$item -operation $operation -taskSpecName 'DesktopRebalanceSpec' -desktopId $item if ($null -ne $spec) { # make sure current task on VMs, must be None - $desktop_helper.Desktop_Rebalance($services,$item,$spec) + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($poolList.$item)) { + $desktop_helper.Desktop_Rebalance($services,$item,$spec) + } + Write-Host "Performed rebalance task on Pool: " $PoolList.$item } } 'REFRESH' { $spec = Get-HVTaskSpec -Source $poolSource.$item -poolName $poolList.$item -operation $operation -taskSpecName 'DesktopRefreshSpec' -desktopId $item if ($null -ne $spec) { # make sure current task on VMs, must be None - $desktop_helper.Desktop_Refresh($services,$item,$spec) + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($poolList.$item)) { + $desktop_helper.Desktop_Refresh($services,$item,$spec) + } + Write-Host "Performed refresh task on Pool: " $PoolList.$item } } 'RECOMPOSE' { @@ -4823,8 +6832,10 @@ function Start-HVPool { $updates = @() $updates += Get-MapEntry -key 'automatedDesktopData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.parentVm' -value $spec.ParentVM $updates += Get-MapEntry -key 'automatedDesktopData.virtualCenterProvisioningSettings.virtualCenterProvisioningData.snapshot' -value $spec.Snapshot - $desktop_helper.Desktop_Update($services,$item,$updates) - + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($poolList.$item)) { + $desktop_helper.Desktop_Update($services,$item,$updates) + } + Write-Host "Performed recompose task on Pool: " $PoolList.$item } } 'PUSH_IMAGE' { @@ -4839,7 +6850,10 @@ function Start-HVPool { $spec.Settings.LogoffSetting = $logoffSetting $spec.Settings.StopOnFirstError = $stopOnFirstError if ($startTime) { $spec.Settings.startTime = $startTime } - $desktop_helper.Desktop_SchedulePushImage($services,$item,$spec) + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($poolList.$item)) { + $desktop_helper.Desktop_SchedulePushImage($services,$item,$spec) + } + Write-Host "Performed push_image task on Pool: " $PoolList.$item } } 'CANCEL_PUSH_IMAGE' { @@ -4847,7 +6861,10 @@ function Start-HVPool { Write-Error "$poolList.$item is not a INSTANT CLONE pool" break } else { - $desktop_helper.Desktop_CancelScheduledPushImage($services,$item) + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($poolList.$item)) { + $desktop_helper.Desktop_CancelScheduledPushImage($services,$item) + } + Write-Host "Performed cancel_push_image task on Pool: " $PoolList.$item } } } @@ -4954,9 +6971,9 @@ function Find-HVMachine { try { if ($params['PoolName']) { - $poolObj = Get-HVPoolSummary -poolName $params['PoolName'] -hvServer $params['HvServer'] + $poolObj = Get-HVPoolSummary -poolName $params['PoolName'] -suppressInfo $true -hvServer $params['HvServer'] if ($poolObj.Length -ne 1) { - Write-Host "Failed to retrieve specific pool object with given PoolName : "$params['PoolName'] + Write-Host "Failed to retrieve specific pool object with given PoolName : " $params['PoolName'] break; } else { $desktopId = $poolObj.Id @@ -5009,25 +7026,39 @@ function Find-HVMachine { $andFilter.Filters = $filterset $query.Filter = $andFilter } - $queryResults = $query_service_helper.QueryService_Query($services,$query) - $machineList = $queryResults.results + $machineList = @() + $GetNext = $false + $queryResults = $query_service_helper.QueryService_Create($services, $query) + do { + if ($GetNext) { $queryResults = $query_service_helper.QueryService_GetNext($services, $queryResults.id) } + $machineList += $queryResults.results + $GetNext = $true + } while ($queryResults.remainingCount -gt 0) + $query_service_helper.QueryService_Delete($services, $queryResults.id) } if ($wildcard -or [string]::IsNullOrEmpty($machineList)) { $query.Filter = $null - $queryResults = $query_service_helper.QueryService_Query($services,$query) - $strFilterSet = @() - foreach ($setting in $machineSelectors.Keys) { - if ($null -ne $params[$setting]) { - if ($wildcard -and (($setting -eq 'MachineName') -or ($setting -eq 'DnsName')) ) { - $strFilterSet += '($_.' + $machineSelectors[$setting] + ' -like "' + $params[$setting] + '")' - } else { - $strFilterSet += '($_.' + $machineSelectors[$setting] + ' -eq "' + $params[$setting] + '")' + $machineList = @() + $GetNext = $false + $queryResults = $query_service_helper.QueryService_Create($services,$query) + do { + if ($GetNext) { $queryResults = $query_service_helper.QueryService_GetNext($services, $queryResults.id) } + $strFilterSet = @() + foreach ($setting in $machineSelectors.Keys) { + if ($null -ne $params[$setting]) { + if ($wildcard -and (($setting -eq 'MachineName') -or ($setting -eq 'DnsName')) ) { + $strFilterSet += '($_.' + $machineSelectors[$setting] + ' -like "' + $params[$setting] + '")' + } else { + $strFilterSet += '($_.' + $machineSelectors[$setting] + ' -eq "' + $params[$setting] + '")' + } } } - } - $whereClause = [string]::Join(' -and ', $strFilterSet) - $scriptBlock = [Scriptblock]::Create($whereClause) - $machineList = $queryResults.results | where $scriptBlock + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $machineList += $queryResults.results | where $scriptBlock + $GetNext = $true + } while ($queryResults.remainingCount -gt 0) + $query_service_helper.QueryService_Delete($services, $queryResults.id) } return $machineList } @@ -5065,19 +7096,23 @@ function Get-HVMachine { .PARAMETER HvServer Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE - Get-HVDesktop -PoolName 'ManualPool' + Get-HVMachine -PoolName 'ManualPool' + Queries VM(s) with given parameter poolName .EXAMPLE - Get-HVDesktop -MachineName 'PowerCLIVM' + Get-HVMachine -MachineName 'PowerCLIVM' + Queries VM(s) with given parameter machineName .EXAMPLE - Get-HVDesktop -State CUSTOMIZING + Get-HVMachine -State CUSTOMIZING + Queries VM(s) with given parameter vm state .EXAMPLE - Get-HVDesktop -DnsName 'powercli-*' -State CUSTOMIZING + Get-HVMachine -DnsName 'powercli-*' + Queries VM(s) with given parameter dnsName with wildcard character * .OUTPUTS Returns list of objects of type MachineInfo @@ -5088,8 +7123,8 @@ function Get-HVMachine { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -5137,7 +7172,7 @@ function Get-HVMachine { $machineList = Find-HVMachine -Param $PSBoundParameters if (!$machineList) { - Write-Host "No Virtual Machine(s) Found with given search parameters" + Write-Host "Get-HVMachine: No Virtual Machine(s) Found with given search parameters" break } $queryResults = @() @@ -5180,21 +7215,28 @@ function Get-HVMachineSummary { If the value is null or not provided then filter will not be applied, otherwise the virtual machines which has display name same as value will be returned. +.PARAMETER SuppressInfo + Suppress text info, when no machine found with given search parameters + .PARAMETER HvServer Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then - first element from global:DefaultHVServers would be considered inplace of hvServer + first element from global:DefaultHVServers would be considered in-place of hvServer .EXAMPLE - Get-HVDesktopSummary -PoolName 'ManualPool' + Get-HVMachineSummary -PoolName 'ManualPool' + Queries VM(s) with given parameter poolName .EXAMPLE - Get-HVDesktopSummary -MachineName 'PowerCLIVM' + Get-HVMachineSummary -MachineName 'PowerCLIVM' + Queries VM(s) with given parameter machineName .EXAMPLE - Get-HVDesktopSummary -State CUSTOMIZING + Get-HVMachineSummary -State CUSTOMIZING + Queries VM(s) with given parameter vm state .EXAMPLE - Get-HVDesktopSummary -DnsName 'powercli-*' -State CUSTOMIZING + Get-HVMachineSummary -DnsName 'powercli-*' + Queries VM(s) with given parameter dnsName with wildcard character * .OUTPUTS Returns list of objects of type MachineNamesView @@ -5205,8 +7247,8 @@ function Get-HVMachineSummary { Version : 1.1 ===Tested Against Environment==== - Horizon View Server Version : 7.0.2, 7.0.3 - PowerCLI Version : PowerCLI 6.5 + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 PowerShell Version : 5.0 #> @@ -5242,6 +7284,10 @@ function Get-HVMachineSummary { [string] $JsonFilePath, + [Parameter(Mandatory = $false)] + [boolean] + $SuppressInfo = $false, + [Parameter(Mandatory = $false)] $HvServer = $null ) @@ -5253,8 +7299,4183 @@ function Get-HVMachineSummary { } $machineList = Find-HVMachine -Param $PSBoundParameters + if (!$machineList -and !$SuppressInfo) { + Write-Host "Get-HVMachineSummary: No machine(s) found with given search parameters" + } return $machineList } -Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool +function Get-HVPoolSpec { +<# +.Synopsis + Gets desktop specification +.DESCRIPTION + Converts DesktopInfo Object to DesktopSpec. Also Converts view API Ids to human readable names + +.PARAMETER DesktopInfo + An object with detailed description of a desktop instance. + +.PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + +.EXAMPLE + Get-HVPoolSpec -DesktopInfo $DesktopInfoObj + Converts DesktopInfo to DesktopSpec + +.EXAMPLE + Get-HVPool -PoolName 'LnkClnJson' | Get-HVPoolSpec -FilePath "C:\temp\LnkClnJson.json" + Converts DesktopInfo to DesktopSpec and also dumps json object + +.OUTPUTS + Returns desktop specification + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [VMware.HV.DesktopInfo] + $DesktopInfo, + + [Parameter(Mandatory = $false)] + [String] + $FilePath, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + $DesktopSpec = New-Object VMware.HV.DesktopSpec + $DesktopPsObj = (($DesktopSpec | ConvertTo-Json -Depth 14) | ConvertFrom-Json) + $DesktopInfoPsObj = (($DesktopInfo | ConvertTo-Json -Depth 14) | ConvertFrom-Json) + $DesktopPsObj.Type = $DesktopInfoPsObj.Type + $DesktopPsObj.DesktopSettings = $DesktopInfoPsObj.DesktopSettings + + $entityId = New-Object VMware.HV.EntityId + $entityId.Id = $DesktopInfoPsObj.Base.AccessGroup.Id + $DesktopPsObj.Base = New-Object PsObject -Property @{ + name = $DesktopInfoPsObj.Base.Name; + displayName = $DesktopInfoPsObj.Base.displayName; + accessGroup = (Get-HVInternalName -EntityId $entityId); + description = $DesktopInfoPsObj.Base.description; + } + + if (! $DesktopInfoPsObj.GlobalEntitlementData.GlobalEntitlement) { + $DesktopPsObj.GlobalEntitlementData = $null + } else { + $entityId.Id = $DesktopInfoPsObj.GlobalEntitlementData.GlobalEntitlement.Id + $DesktopPsObj.GlobalEntitlementData = Get-HVInternalName -EntityId $entityId + } + + Switch ($DesktopInfo.Type) { + "AUTOMATED" { + $specificNamingSpecObj = $null + if ("SPECIFIED" -eq $DesktopInfoPsObj.AutomatedDesktopData.vmNamingSettings.NamingMethod) { + $specificNamingSpecObj = New-Object PsObject -Property @{ + specifiedNames = $null; + startMachinesInMaintenanceMode = $DesktopInfoPsObj.AutomatedDesktopData.vmNamingSettings.SpecificNamingSettings.StartMachinesInMaintenanceMode; + numUnassignedMachinesKeptPoweredOn = $DesktopInfoPsObj.AutomatedDesktopData.vmNamingSettings.SpecificNamingSettings.NumUnassignedMachinesKeptPoweredOn; + } + } + $vmNamingSpecObj = New-Object PsObject -Property @{ + namingMethod = $DesktopInfoPsObj.AutomatedDesktopData.vmNamingSettings.NamingMethod; + patternNamingSettings = $DesktopInfoPsObj.AutomatedDesktopData.VmNamingSettings.PatternNamingSettings; + specificNamingSpec = $specificNamingSpecObj; + } + $virtualCenterProvisioningDataObj = New-Object PsObject @{ + template = $null; + parentVm = $null; + snapshot = $null; + datacenter = $null; + vmFolder = $null; + hostOrCluster = $null; + resourcePool= $null; + } + $ProvisioningSettingsObj = $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.Datacenter){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.Datacenter.Id + $virtualCenterProvisioningDataObj.Datacenter = Get-HVInternalName -EntityId $entityId + } + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.HostOrCluster){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.HostOrCluster.Id + $virtualCenterProvisioningDataObj.HostOrCluster = Get-HVInternalName -EntityId $entityId + } + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.ResourcePool){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.ResourcePool.Id + $virtualCenterProvisioningDataObj.ResourcePool = Get-HVInternalName -EntityId $entityId + } + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.ParentVm){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.ParentVm.Id + $virtualCenterProvisioningDataObj.ParentVm = Get-HVInternalName -EntityId $entityId ` + -VcId $DesktopInfo.AutomatedDesktopData.virtualCenter + } + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.Snapshot){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.Snapshot.Id + $virtualCenterProvisioningDataObj.Snapshot = Get-HVInternalName -EntityId $entityId ` + -BaseImageVmId $DesktopInfo.AutomatedDesktopData.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData.ParentVm + } + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.Template){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.Template.Id + $virtualCenterProvisioningDataObj.Template = Get-HVInternalName -EntityId $entityId + } + if ($ProvisioningSettingsObj.VirtualCenterProvisioningData.VmFolder){ + $entityId.Id = $ProvisioningSettingsObj.VirtualCenterProvisioningData.VmFolder.Id + $virtualCenterProvisioningDataObj.VmFolder = Get-HVInternalName -EntityId $entityId + } + + $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings.VirtualCenterProvisioningData = ` + $virtualCenterProvisioningDataObj + $datastores = $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.datastores + $dataStoresObj = Get-DataStoreName -datastores $datastores + $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.datastores = ` + $dataStoresObj + $virtualCenterStorageSettingsObj = ` + $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings.virtualCenterStorageSettings + if($virtualCenterStorageSettingsObj.replicaDiskDatastore) { + $entityId.Id = $virtualCenterStorageSettingsObj.replicaDiskDatastore.Id + $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.replicaDiskDatastore =` + Get-HVInternalName -EntityId $entityId + } + if($virtualCenterStorageSettingsObj.persistentDiskSettings) { + $datastores = $virtualCenterStorageSettingsObj.persistentDiskSettings.persistentDiskDatastores + $dataStoresObj = Get-DataStoreName -datastores $datastores + $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings.virtualCenterStorageSettings.persistentDiskSettings.persistentDiskDatastores = ` + $dataStoresObj + } + if ($DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.domainAdministrator) { + $entityId.Id = $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.domainAdministrator.Id + $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.domainAdministrator = Get-HVInternalName -EntityId $entityId + } + if ($DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.adContainer) { + $entityId.Id = $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.adContainer.Id + $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.adContainer = Get-HVInternalName -EntityId $entityId + } + if ($DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.sysprepCustomizationSettings) { + $entityId.Id = ` + $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.sysprepCustomizationSettings.customizationSpec.Id + $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.sysprepCustomizationSettings.customizationSpec = ` + Get-HVInternalName -EntityId $entityId + } + if ($DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.cloneprepCustomizationSettings) { + $entityId.Id = ` + $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.cloneprepCustomizationSettings.instantCloneEngineDomainAdministrator.Id + $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings.cloneprepCustomizationSettings.instantCloneEngineDomainAdministrator = ` + Get-HVInternalName -EntityId $entityId + } + + $DesktopPsObj.AutomatedDesktopSpec = New-Object PsObject -Property @{ + provisioningType = $DesktopInfoPsObj.AutomatedDesktopData.ProvisioningType; + virtualCenter = $null; + userAssignment = $DesktopInfoPsObj.AutomatedDesktopData.UserAssignment; + virtualCenterProvisioningSettings = $DesktopInfoPsObj.AutomatedDesktopData.VirtualCenterProvisioningSettings; + virtualCenterManagedCommonSettings = $DesktopInfoPsObj.AutomatedDesktopData.virtualCenterManagedCommonSettings; + customizationSettings = $DesktopInfoPsObj.AutomatedDesktopData.customizationSettings; + vmNamingSpec = $VmNamingSpecObj; + } + if ($DesktopInfoPsObj.AutomatedDesktopData.virtualCenter) { + $entityId.Id = $DesktopInfoPsObj.AutomatedDesktopData.virtualCenter.Id + $DesktopPsObj.AutomatedDesktopSpec.virtualCenter = Get-HVInternalName ` + -EntityId $entityId + } + break + } + "MANUAL" { + $DesktopPsObj.ManualDesktopSpec = New-Object PsObject -Property @{ + userAssignment = $DesktopInfoPsObj.ManualDesktopData.UserAssignment; + source = $DesktopInfoPsObj.ManualDesktopData.Source; + virtualCenter = $null; + machines = $null; + viewStorageAcceleratorSettings = $DesktopInfoPsObj.ManualDesktopData.ViewStorageAcceleratorSettings; + virtualCenterManagedCommonSettings = $DesktopInfoPsObj.ManualDesktopData.VirtualCenterManagedCommonSettings; + } + if ($DesktopInfoPsObj.ManualDesktopData.virtualCenter) { + $entityId.Id = $DesktopInfoPsObj.ManualDesktopData.virtualCenter.Id + $DesktopPsObj.ManualDesktopSpec.virtualCenter = Get-HVInternalName ` + -EntityId $entityId + } + break + } + "RDS" { + $DesktopPsObj.rdsDesktopSpec = New-Object PsObject -Property @{ + farm = $null; + } + break + } + } + $DesktopSpecJson = ($DesktopPsObj | ConvertTo-Json -Depth 14) + if ($filePath) { + $DesktopSpecJson | Out-File -FilePath $filePath + } + return $DesktopSpecJson +} + +function Get-DataStoreName { + param( + [Parameter(Mandatory = $true)] + $datastores + ) + $dataStoresObj = @() + $entityId = New-Object VMware.Hv.EntityId + $datastores | % { + $entityId.Id = $_.datastore.Id + $dataStoresObj += , (New-Object PsObject -Property @{ + datastore = Get-HVInternalName -EntityId $entityId; + storageOvercommit = $_.storageOvercommit; + }) + } + return $dataStoresObj +} + +function Get-HVInternalName { +<# +.Synopsis + Gets human readable name + +.DESCRIPTION + Converts Horizon API Ids to human readable names. Horizon API Ids are base64 encoded, this function + will decode and returns internal/human readable names. + +.PARAMETER EntityId + Representation of a manageable entity id. + +.PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + +.EXAMPLE + Get-HVInternalName -EntityId $entityId + Decodes Horizon API Id and returns human readable name + +.OUTPUTS + Returns human readable name + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [VMware.HV.EntityId] + $EntityId, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [VMware.HV.VirtualCenterId] + $VcId, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [VMware.HV.BaseImageVmId] + $BaseImageVmId, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $tokens = ($EntityId.id -split "/") + $serviceName = $tokens[0] + Switch ($serviceName) { + 'VirtualCenter' { + $vc_id = New-Object VMware.HV.VirtualCenterId + $vc_id.Id = $EntityId.Id + return ($services.VirtualCenter.VirtualCenter_Get($vc_id)).serverSpec.serverName + } + 'InstantCloneEngineDomainAdministrator' { + $Icid = New-Object VMware.HV.InstantCloneEngineDomainAdministratorId + $Icid.Id = $EntityId.Id + $Info = $services.InstantCloneEngineDomainAdministrator.InstantCloneEngineDomainAdministrator_Get($Icid) + return $Info.Base.Username + } + 'BaseImageVm' { + $info = $services.BaseImageVm.BaseImageVm_List($VcId) | where { $_.id.id -eq $EntityId.id } + return $info.name + } + 'BaseImageSnapshot' { + $info = $services.BaseImageSnapshot.BaseImageSnapshot_List($BaseImageVmId) | where { $_.id.id -eq $EntityId.id } + return $info.name + } + 'VmTemplate' { + $info = $services.VmTemplate.VmTemplate_List($VcId) | where { $_.id.id -eq $EntityId.id } + return $info.name + } + 'ViewComposerDomainAdministrator' { + $AdministratorId = New-Object VMware.HV.ViewComposerDomainAdministratorId + $AdministratorId.id = $EntityId.id + $info = $services.ViewComposerDomainAdministrator.ViewComposerDomainAdministrator_Get($AdministratorId) + return $info.base.userName + } + default { + $base64String = $tokens[$tokens.Length-1] + $mod = $base64String.Length % 4 + if ($mod -ne 0) { + #Length of a string must be multiples of 4 + $base64String = $base64String.PadRight(($base64String.Length + (4 - $mod)), "=") + } + #Convert 4 bytes to 3 bytes base64 decoding + return ([System.Text.Encoding]::ASCII.GetString([System.Convert]:: ` + FromBase64String($base64String))) + } + } + } + end { + [System.gc]::collect() + } +} + + +function Get-UserInfo { + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true)] + [ValidatePattern("^.+?[@\\].+?$")] + [String] + $UserName + ) + + if ($UserName -match '^.+?[@].+?$') { + $info = $UserName -split "@" + $Domain = $info[1] + $Name = $Info[0] + } else { + $info = $UserName -split "\\" + $Domain = $info[0] + $Name = $Info[1] + } + return @{'Name' = $Name; 'Domain' = $Domain} +} + +function New-HVEntitlement { +<# +.Synopsis + Associates a user/group with a resource + +.DESCRIPTION + This represents a simple association between a single user/group and a resource that they can be assigned. + +.PARAMETER User + User principal name of user or group + +.PARAMETER ResourceName + The resource(Application, Desktop etc.) name. + Supports only wildcard character '*' when resource type is desktop. + +.PARAMETER Resource + Object(s) of the resource(Application, Desktop etc.) to entitle + +.PARAMETER ResourceType + Type of Resource(Application, Desktop etc) + +.PARAMETER Type + Whether or not this is a group or a user. + +.PARAMETER HvServer + Reference to Horizon View Server. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + +.EXAMPLE + New-HVEntitlement -User 'administrator@adviewdev.eng.vmware.com' -ResourceName 'InsClnPol' -Confirm:$false + Associate a user/group with a pool + +.EXAMPLE + New-HVEntitlement -User 'adviewdev\administrator' -ResourceName 'Calculator' -ResourceType Application + Associate a user/group with a application + +.EXAMPLE + New-HVEntitlement -User 'adviewdev.eng.vmware.com\administrator' -ResourceName 'UrlSetting1' -ResourceType URLRedirection + Associate a user/group with a URLRedirection settings + +.EXAMPLE + New-HVEntitlement -User 'adviewdev.eng.vmware.com\administrator' -ResourceName 'GE1' -ResourceType GlobalEntitlement + Associate a user/group with a desktop entitlement + +.EXAMPLE + New-HVEntitlement -User 'adviewdev\administrator' -ResourceName 'GEAPP1' -ResourceType GlobalApplicationEntitlement + Associate a user/group with a application entitlement + +.EXAMPLE + $pools = Get-HVPool; $pools | New-HVEntitlement -User 'adviewdev\administrator' -Confirm:$false + Associate a user/group with list of pools + + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true)] + [ValidatePattern("^.+?[@\\].+?$")] + [String] + $User, + + [Parameter(Mandatory = $true,ParameterSetName ='Default')] + [ValidateNotNullOrEmpty()] + [String] + $ResourceName, + + [Parameter(Mandatory = $true,ValueFromPipeline = $true,ParameterSetName ='PipeLine')] + $Resource, + + [Parameter(Mandatory = $false)] + [ValidateSet('Application','Desktop','GlobalApplicationEntitlement','GlobalEntitlement', + 'URLRedirection')] + [String] + $ResourceType = 'Desktop', + + [Parameter(Mandatory = $false)] + [ValidateSet('User','Group')] + [String] + $Type = 'User', + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys + $userInfo = Get-UserInfo -UserName $User + $UserOrGroupName = $userInfo.Name + $Domain = $userInfo.Domain + $IsGroup = ($Type -eq 'Group') + $filter1 = Get-HVQueryFilter 'base.name' -Eq $UserOrGroupName + $filter2 = Get-HVQueryFilter 'base.domain' -Eq $Domain + $filter3 = Get-HVQueryFilter 'base.group' -Eq $IsGroup + $andFilter = Get-HVQueryFilter -And -Filters @($filter1, $filter2, $filter3) + $results = Get-HVQueryResult -EntityType ADUserOrGroupSummaryView -Filter $andFilter -HvServer $HvServer + if ($results.length -ne 1) { + Write-Host "Unable to find specific user or group with given search parameters" + return + } + $ResourceObjs = $null + $info = $services.PodFederation.PodFederation_get() + switch($ResourceType){ + "Desktop" { + if ($ResourceName) { + $ResourceObjs = Get-HVPool -PoolName $ResourceName -suppressInfo $true -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No pool found with given resourceName: " $ResourceName + return + } + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Resource) { + foreach ($item in $Resource) { + if ($item.GetType().name -eq 'DesktopInfo') { + $ResourceObjs += ,$item + } + elseif ($item.GetType().name -eq 'DesktopSummaryView') { + $ResourceObjs += ,$item + } + else { + Write-Error "In pipeline didn't received object(s) of expected type DesktopSummaryView/DesktopInfo" + return + } + } + } + } + "Application" { + if ($ResourceName) { + $eqFilter = Get-HVQueryFilter 'data.name' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType ApplicationInfo -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No Application found with given resourceName: " $ResourceName + return + } + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Resource) { + foreach ($item in $Resource) { + if ($item.GetType().name -eq 'ApplicationInfo') { + $ResourceObjs += ,$item + + } else { + Write-Error "In pipeline didn't received object(s) of expected type ApplicationInfo" + return + } + } + } + } + "URLRedirection" { + if ($ResourceName) { + $UrlRedirectionList = $services.URLRedirection.URLRedirection_List() + $ResourceObjs = $UrlRedirectionList | Where-Object { $_.urlRedirectionData.displayName -like $ResourceName} + if (! $ResourceObjs) { + Write-Host "No URLRedirectionData found with given resourceName: " $ResourceName + return + } + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Resource) { + foreach ($item in $Resource) { + if ($item.GetType().name -eq 'URLRedirectionInfo') { + $ResourceObjs += ,$item + } else { + Write-Error "In pipeline didn't received object(s) of expected type URLRedirectionInfo" + return + } + } + } + } + "GlobalApplicationEntitlement" { + if ("ENABLED" -eq $info.localPodStatus.status) { + if ($ResourceName) { + $eqFilter = Get-HVQueryFilter 'base.displayName' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType GlobalApplicationEntitlementInfo -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No globalApplicationEntitlementInfo found with given resourceName: " $ResourceName + return + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Resource) { + foreach ($item in $Resource) { + if ($item.GetType().name -eq 'GlobalApplicationEntitlementInfo') { + $ResourceObjs += ,$item + } else { + Write-Error "In pipeline didn't received object(s) of expected type globalApplicationEntitlementInfo" + return + } + } + } + } + } else { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + } + "GlobalEntitlement" { + if ("ENABLED" -eq $info.localPodStatus.status) { + if ($ResourceName) { + $eqFilter = Get-HVQueryFilter 'base.displayName' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType GlobalEntitlementSummaryView -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No globalEntitlementSummary found with given resourceName: " $ResourceName + return + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Resource) { + foreach ($item in $Resource) { + if ($item.GetType().name -eq 'GlobalEntitlementSummaryView') { + $ResourceObjs += ,$item + } else { + Write-Error "In pipeline didn't received object(s) of expected type GlobalEntitlementSummaryView" + return + } + } + } + } + } else { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + } + } + $base = New-Object VMware.HV.UserEntitlementBase + $base.UserOrGroup = $results.id + Write-host $ResourceObjs.Length " resource(s) will be entitled with UserOrGroup: " $User + foreach ($ResourceObj in $ResourceObjs) { + $base.Resource = $ResourceObj.id + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { + $id = $services.UserEntitlement.UserEntitlement_Create($base) + } + } + } + end { + [System.gc]::collect() + } +} + + +function Get-HVEntitlement { +<# +.Synopsis + Gets association data between a user/group and a resource + +.DESCRIPTION + Provides entitlement Info between a single user/group and a resource that they can be assigned. + +.PARAMETER User + User principal name of user or group + +.PARAMETER ResourceName + The resource(Application, Desktop etc.) name. + Supports only wildcard character '*' when resource type is desktop. + +.PARAMETER Resource + Object(s) of the resource(Application, Desktop etc.) to entitle + +.PARAMETER ResourceType + Type of Resource(Application, Desktop etc.) + +.PARAMETER Type + Whether or not this is a group or a user. + +.PARAMETER HvServer + Reference to Horizon View Server. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + +.EXAMPLE + Get-HVEntitlement -ResourceType Application + Gets all the entitlements related to application pool + +.EXAMPLE + Get-HVEntitlement -User 'adviewdev.eng.vmware.com\administrator' -ResourceName 'calculator' -ResourceType Application + Gets entitlements specific to user or group name and application resource + +.EXAMPLE + Get-HVEntitlement -User 'adviewdev.eng.vmware.com\administrator' -ResourceName 'UrlSetting1' -ResourceType URLRedirection + Gets entitlements specific to user or group and URLRedirection resource + +.EXAMPLE + Get-HVEntitlement -User 'administrator@adviewdev.eng.vmware.com' -ResourceName 'GE1' -ResourceType GlobalEntitlement + Gets entitlements specific to user or group and GlobalEntitlement resource + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $false)] + [ValidatePattern("^.+?[@\\].+?$")] + [String] + $User, + + [Parameter(Mandatory = $false)] + [ValidateSet('User','Group')] + [String] + $Type = 'User', + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [String] + $ResourceName, + + [Parameter(Mandatory = $false)] + [ValidateSet('Application','Desktop','GlobalApplicationEntitlement','GlobalEntitlement', + 'URLRedirection')] + [String] + $ResourceType = 'Desktop', + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $AndFilter = @() + $results = @() + $ResourceObjs = $null + if ($User) { + $userInfo = Get-UserInfo -UserName $User + $UserOrGroupName = $userInfo.Name + $Domain = $userInfo.Domain + $nameFilter = Get-HVQueryFilter 'base.name' -Eq $UserOrGroupName + $AndFilter += $nameFilter + $doaminFilter = Get-HVQueryFilter 'base.domain' -Eq $Domain + $AndFilter += $doaminFilter + } + if ($type -eq 'group'){ + $IsGroup = ($Type -eq 'Group') + $groupFilter = Get-HVQueryFilter 'base.group' -Eq $IsGroup + $AndFilter += $groupFilter + } + $info = $services.PodFederation.PodFederation_get() + $cpaEnabled = ("ENABLED" -eq $info.localPodStatus.status) + switch($ResourceType) { + "Desktop" { + if ($ResourceName) { + $ResourceObjs = Get-HVPool -PoolName $ResourceName -suppressInfo $true -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No pool found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'localData.desktops' -Contains ([VMware.Hv.DesktopId[]]$ResourceObjs.Id) + } + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = (Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $AndFilter -HvServer $HvServer) + $results = $results | where {$_.localData.desktops -ne $null} + } + "Application" { + if ($ResourceName) { + $eqFilter = Get-HVQueryFilter 'data.name' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType ApplicationInfo -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No Application found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'localData.applications' -Contains ([VMware.Hv.ApplicationId[]]$ResourceObjs.Id) + } + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = (Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $AndFilter -HvServer $HvServer) + $results = $results | where {$_.localData.applications -ne $null} + } + "URLRedirection" { + $localFilter = @() + $globalFilter = @() + $localFilter += $AndFilter + $globalFilter += $AndFilter + if ($ResourceName) { + $UrlRedirectionList = $services.URLRedirection.URLRedirection_List() + $ResourceObjs = $UrlRedirectionList | Where-Object { $_.urlRedirectionData.displayName -like $ResourceName} + if (! $ResourceObjs) { + Write-Host "No URLRedirectionData found with given resourceName: " $ResourceName + return + } + $localFilter += Get-HVQueryFilter 'localData.urlRedirectionSettings' -Contains ([VMware.Hv.URLRedirectionId[]]$ResourceObjs.Id) + if ($cpaEnabled) { + $globalFilter += Get-HVQueryFilter 'globalData.urlRedirectionSettings' -Contains ([VMware.Hv.URLRedirectionId[]]$ResourceObjs.Id) + } + } + $localFilter = Get-HVQueryFilter -And -Filters $localFilter + $localResults = Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $localFilter -HvServer $HvServer + $results += ($localResults | where {$_.localData.urlRedirectionSettings -ne $null}) + if ($cpaEnabled) { + $globalFilter = Get-HVQueryFilter -And -Filters $globalFilter + $globalResults = Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $globalFilter -HvServer $HvServer + $globalResults = $globalResults | where {$_.globalData.urlRedirectionSettings -ne $null} + $results += $globalResults + } + } + "GlobalApplicationEntitlement" { + if (! $cpaEnabled) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + if ($ResourceName) { + $eqFilter = Get-HVQueryFilter 'base.displayName' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType GlobalApplicationEntitlementInfo -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No globalApplicationEntitlementInfo found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'globalData.globalApplicationEntitlements' -Contains ([VMware.Hv.GlobalApplicationEntitlementId[]]$ResourceObjs.Id) + } + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = (Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $AndFilter -HvServer $HvServer) + $results = $results| where {$_.globalData.globalApplicationEntitlements -ne $null} + } + "GlobalEntitlement" { + if (! $cpaEnabled) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + if ($ResourceName) { + $eqFilter = Get-HVQueryFilter 'base.displayName' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType GlobalEntitlementSummaryView -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No globalEntitlementSummary found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'globalData.globalEntitlements' -Contains ([VMware.Hv.GlobalEntitlementId[]]$ResourceObjs.Id) + } + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = (Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $AndFilter -HvServer $HvServer) + $results = $results | where {$_.globalData.globalEntitlements -ne $null} + } + } + if (! $results) { + Write-Host "Get-HVEntitlement: No entitlements found with given search parameters" + } + return $results + } + end { + [System.gc]::collect() + } +} + +function Remove-HVEntitlement { +<# +.Synopsis + Deletes association data between a user/group and a resource + +.DESCRIPTION + Removes entitlement between a single user/group and a resource that already been assigned. + +.PARAMETER User + User principal name of user or group + +.PARAMETER ResourceName + The resource(Application, Desktop etc.) name. + Supports only wildcard character '*' when resource type is desktop. + +.PARAMETER Resource + Object(s) of the resource(Application, Desktop etc.) to entitle + +.PARAMETER ResourceType + Type of Resource(Application, Desktop etc) + +.PARAMETER Type + Whether or not this is a group or a user. + +.PARAMETER HvServer + Reference to Horizon View Server. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + +.EXAMPLE + Remove-HVEntitlement -User 'administrator@adviewdev' -ResourceName LnkClnJSon -Confirm:$false + Deletes entitlement between a user/group and a pool resource + +.EXAMPLE + Remove-HVEntitlement -User 'adviewdev\puser2' -ResourceName 'calculator' -ResourceType Application + Deletes entitlement between a user/group and a Application resource + +.EXAMPLE + Remove-HVEntitlement -User 'adviewdev\administrator' -ResourceName 'GEAPP1' -ResourceType GlobalApplicationEntitlement + Deletes entitlement between a user/group and a GlobalApplicationEntitlement resource + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true)] + [ValidatePattern("^.+?[@\\].+?$")] + [String] + $User, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $ResourceName, + + [Parameter(Mandatory = $false)] + [ValidateSet('User','Group')] + [String] + $Type = 'User', + + [Parameter(Mandatory = $false)] + [ValidateSet('Application','Desktop','GlobalApplicationEntitlement','GlobalEntitlement', + 'URLRedirection')] + [String] + $ResourceType = 'Desktop', + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys + $AndFilter = @() + $results = $null + if ($User) { + $userInfo = Get-UserInfo -UserName $User + $AndFilter += Get-HVQueryFilter 'base.loginName' -Eq $userInfo.Name + $AndFilter += Get-HVQueryFilter 'base.domain' -Eq $userInfo.Domain + } + $AndFilter += Get-HVQueryFilter 'base.group' -Eq ($Type -eq 'Group') + [VMware.Hv.UserEntitlementId[]] $userEntitlements = $null + if ($ResourceName) { + $info = $services.PodFederation.PodFederation_get() + switch($ResourceType) { + "Desktop" { + $ResourceObjs = Get-HVPool -PoolName $ResourceName -suppressInfo $true -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No pool found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'localData.desktops' -Contains ([VMware.HV.DesktopId[]] $ResourceObjs.Id) + $filters = Get-HVQueryFilter -And -Filters $AndFilter + $results = Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $filters -HvServer $HvServer + if ($results) { + foreach ($result in $Results) { + $deleteResources = @() + for ($i = 0; $i -lt $result.localdata.desktops.length; $i++) { + if ($ResourceObjs.Id.id -eq $result.localdata.Desktops[$i].id) { + $deleteResources += $result.localdata.DesktopUserEntitlements[$i] + } + } + Write-Host $deleteResources.Length " desktopUserEntitlement(s) will be removed for UserOrGroup " $user + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { + $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($deleteResources) + } + } + } + } + "Application" { + $eqFilter = Get-HVQueryFilter 'data.name' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType ApplicationInfo -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No Application found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'localData.applications' -Contains ([VMware.HV.ApplicationId[]] $ResourceObjs.Id) + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $AndFilter -HvServer $HvServer + if ($results) { + foreach ($result in $Results) { + $userEntitlements = $result.localData.applicationUserEntitlements + Write-Host $userEntitlements.Length " applicationUserEntitlement(s) will be removed for UserOrGroup " $user + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { + $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) + } + } + } + } + "URLRedirection" { + $UrlRedirectionList = $services.URLRedirection.URLRedirection_List() + $ResourceObjs = $UrlRedirectionList | Where-Object { $_.urlRedirectionData.displayName -like $ResourceName} + if (! $ResourceObjs) { + Write-Host "No URLRedirectionData found with given resourceName: " $ResourceName + return + } + $localFilter = @() + $localFilter += $AndFilter + $localFilter += (Get-HVQueryFilter 'localData.urlRedirectionSettings' -Contains ([VMware.HV.URLRedirectionId[]]$ResourceObjs.Id)) + $localFilter = Get-HVQueryFilter -And -Filters $localFilter + $results = Get-HVQueryResult -EntityType EntitledUserOrGroupLocalSummaryView -Filter $localFilter -HvServer $HvServer + if ("ENABLED" -eq $info.localPodStatus.status) { + $globalFilter = @() + $globalFilter += $AndFilter + $globalFilter += Get-HVQueryFilter 'globalData.urlRedirectionSettings' -Contains ([VMware.HV.URLRedirectionId[]]$ResourceObjs.Id) + $globalFilter = Get-HVQueryFilter -And -Filters $globalFilter + $results += Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $globalFilter -HvServer $HvServer + } + if ($results) { + foreach ($result in $Results) { + if ($result.GetType().Name -eq 'EntitledUserOrGroupLocalSummaryView') { + $userEntitlements = $result.localData.urlRedirectionUserEntitlements + Write-Host $userEntitlements.Length " urlRedirectionUserEntitlement(s) will be removed for UserOrGroup " $user + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { + $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) + } + } else { + $userEntitlements = $result.globalData.urlRedirectionUserEntitlements + Write-Host $userEntitlements.Length " urlRedirectionUserEntitlement(s) will be removed for UserOrGroup " $user + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { + $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) + } + } + } + } + } + "GlobalApplicationEntitlement" { + if ("ENABLED" -ne $info.localPodStatus.status) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + $eqFilter = Get-HVQueryFilter 'base.displayName' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType GlobalApplicationEntitlementInfo -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No globalApplicationEntitlementInfo found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'globalData.globalApplicationEntitlements' -Contains ([VMware.Hv.GlobalApplicationEntitlementId[]]$ResourceObjs.Id) + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $AndFilter -HvServer $HvServer + if ($results) { + foreach ($result in $Results) { + $userEntitlements = $result.globalData.globalUserApplicationEntitlements + Write-Host $userEntitlements.Length " GlobalApplicationEntitlement(s) will be removed for UserOrGroup " $user + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { + $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($userEntitlements) + } + } + } + } + "GlobalEntitlement" { + if ("ENABLED" -ne $info.localPodStatus.status) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + $eqFilter = Get-HVQueryFilter 'base.displayName' -Eq $ResourceName + $ResourceObjs = Get-HVQueryResult -EntityType GlobalEntitlementSummaryView -Filter $eqFilter -HvServer $HvServer + if (! $ResourceObjs) { + Write-Host "No globalEntitlementSummary found with given resourceName: " $ResourceName + return + } + $AndFilter += Get-HVQueryFilter 'globalData.globalEntitlements' -Contains ([VMware.Hv.GlobalEntitlementId[]]$ResourceObjs.Id) + $AndFilter = Get-HVQueryFilter -And -Filters $AndFilter + $results = Get-HVQueryResult -EntityType EntitledUserOrGroupGlobalSummaryView -Filter $AndFilter -HvServer $HvServer + if ($results) { + foreach ($result in $Results) { + $deleteResources = @() + for ($i = 0; $i -lt $result.globalData.globalEntitlements.length; $i++) { + if ($ResourceObjs.Id.id -eq $result.globalData.globalEntitlements[$i].id) { + $deleteResources += $result.globalData.globalUserEntitlements[$i] + } + } + Write-Host $deleteResources.Length " GlobalEntitlement(s) will be removed for UserOrGroup " $user + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($User)) { + $services.UserEntitlement.UserEntitlement_DeleteUserEntitlements($deleteResources) + } + } + + } + } + } + } + if (! $results) { + Write-Host "Remove-HVEntitlement: No entitlements found with given search parameters" + return + } + } + end { + [System.gc]::collect() + } +} + +function Set-HVMachine { +<# +.Synopsis + Sets existing virtual Machine(s). + +.DESCRIPTION + This cmdlet allows user to edit Machine configuration by passing key/value pair. + Allows the machine in to Maintenance mode and vice versa + +.PARAMETER MachineName + The name of the Machine to edit. + +.PARAMETER Machine + Object(s) of the virtual Machine(s) to edit. + +.PARAMETER Maintenance + The virtual machine is in maintenance mode. Users cannot log in or use the virtual machine + +PARAMETER Key + Property names path separated by . (dot) from the root of machine info spec. + +.PARAMETER Value + Property value corresponds to above key name. + +.PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + +.EXAMPLE + Set-HVMachine -MachineName 'Agent_Praveen' -Maintenance ENTER_MAINTENANCE_MODE + Moving the machine in to Maintenance mode using machine name + +.EXAMPLE + Get-HVMachine -MachineName 'Agent_Praveen' | Set-HVMachine -Maintenance ENTER_MAINTENANCE_MODE + Moving the machine in to Maintenance mode using machine object(s) + +.EXAMPLE + $machine = Get-HVMachine -MachineName 'Agent_Praveen'; Set-HVMachine -Machine $machine -Maintenance EXIT_MAINTENANCE_MODE + Moving the machine in to Maintenance mode using machine object(s) + +.OUTPUTS + None + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + + [Parameter(Mandatory = $true ,ParameterSetName = 'option')] + [string] + $MachineName, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'pipeline')] + $Machine, + + [Parameter(Mandatory = $false)] + [ValidateSet('ENTER_MAINTENANCE_MODE', 'EXIT_MAINTENANCE_MODE')] + [string] + $Maintenance, + + [Parameter(Mandatory = $false)] + [string]$Key, + + [Parameter(Mandatory = $false)] + $Value, + + [Parameter(Mandatory = $false)] + [ValidatePattern("^.+?[@\\].+?$")] + [string] + $User, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + + process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys + $machineList = @{} + if ($machineName) { + try { + $machines = Get-HVMachineSummary -MachineName $machineName -suppressInfo $true -hvServer $hvServer + } catch { + Write-Error "Make sure Get-HVMachineSummary advanced function is loaded, $_" + break + } + if ($machines) { + foreach ($macineObj in $machines) { + $machineList.add($macineObj.id, $macineObj.base.Name) + } + } + if ($machineList.count -eq 0) { + Write-Error "Machine $machineName not found - try fqdn" + [System.gc]::collect() + return + } + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $Machine) { + foreach ($item in $machine) { + if (($item.GetType().name -eq 'MachineNamesView') -or ($item.GetType().name -eq 'MachineInfo')) { + $machineList.add($item.id, $item.Base.Name) + } else { + Write-Error "In pipeline did not get object of expected type MachineNamesView/MachineInfo" + [System.gc]::collect() + return + } + } + } + $updates = @() + if ($key -and $value) { + $updates += Get-MapEntry -key $key -value $value + } elseif ($key -or $value) { + Write-Error "Both key:[$key] and value:[$value] needs to be specified" + } + if ($User) { + $userInfo = Get-UserInfo -UserName $User + $UserOrGroupName = $userInfo.Name + $Domain = $userInfo.Domain + $filter1 = Get-HVQueryFilter 'base.name' -Eq $UserOrGroupName + $filter2 = Get-HVQueryFilter 'base.domain' -Eq $Domain + $filter3 = Get-HVQueryFilter 'base.group' -Eq $false + $andFilter = Get-HVQueryFilter -And -Filters @($filter1, $filter2, $filter3) + $results = Get-HVQueryResult -EntityType ADUserOrGroupSummaryView -Filter $andFilter -HvServer $HvServer + if ($results.length -ne 1) { + Write-Host "Unable to find specific user with given search parameters" + [System.gc]::collect() + return + } + $updates += Get-MapEntry -key 'base.user' -value $results[0].id + } + + if ($Maintenance) { + if ($Maintenance -eq 'ENTER_MAINTENANCE_MODE') { + $updates += Get-MapEntry -key 'managedMachineData.inMaintenanceMode' -value $true + } else { + $updates += Get-MapEntry -key 'managedMachineData.inMaintenanceMode' -value $false + } + } + $machine_helper = New-Object VMware.Hv.MachineService + foreach ($item in $machineList.Keys) { + Write-Host "Updating the Machine: " $machineList.$item + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($machineList.$item)) { + $machine_helper.Machine_Update($services,$item,$updates) + } + } + } + + end { + [System.gc]::collect() + } + +} + +function New-HVGlobalEntitlement { + + <# +.Synopsis + Creates a Global Entitlement. + +.DESCRIPTION + Global entitlements are used to route users to their resources across multiple pods. + These are persisted in a global ldap instance that is replicated across all pods in a linked mode view set. + +.PARAMETER DisplayName + Display Name of Global Entitlement. + +.PARAMETER Type + Specify whether to create desktop/app global entitlement + +.PARAMETER Description + Description of Global Entitlement. + +.PARAMETER Scope + Scope for this global entitlement. Visibility and Placement policies are defined by this value. + +.PARAMETER Dedicated + Specifies whether dedicated/floating resources associated with this global entitlement. + +.PARAMETER FromHome + This value defines the starting location for resource placement and search. + When true, a pod in the user's home site is used to start the search. When false, the current site is used. + +.PARAMETER RequireHomeSite + This value determines whether we fail if a home site isn't defined for this global entitlement. + +.PARAMETER MultipleSessionAutoClean + This value is used to determine if automatic session clean up is enabled. + This cannot be enabled when this Global Entitlement is associated with a Desktop that has dedicated user assignment. + +.PARAMETER Enabled + If this Global Entitlement is enabled. + +.PARAMETER SupportedDisplayProtocols + The set of supported display protocols for the global entitlement. + +.PARAMETER DefaultDisplayProtocol + The default display protocol for the global entitlement. + +.PARAMETER AllowUsersToChooseProtocol + Whether the users can choose the protocol used. + +.PARAMETER AllowUsersToResetMachines + Whether users are allowed to reset/restart their machines. + +.PARAMETER EnableHTMLAccess + If set to true, the desktops that are associated with this GlobalEntitlement must also have HTML Access enabled. + +.PARAMETER HvServer + Reference to Horizon View Server. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + +.EXAMPLE + New-HVGlobalEntitlement -DisplayName 'GE_APP' -Type APPLICATION_ENTITLEMENT + Creates new global application entitlement + +.EXAMPLE + New-HVGlobalEntitlement -DisplayName 'GE_DESKTOP' -Type DESKTOP_ENTITLEMENT + Creates new global desktop entitlement + + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + +[CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $DisplayName, + + [Parameter(Mandatory = $true)] + [ValidateSet('DESKTOP_ENTITLEMENT','APPLICATION_ENTITLEMENT')] + [String] + $Type, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [String] + $Description, + + [Parameter(Mandatory = $false)] + [ValidateSet('LOCAL','SITE','ANY')] + [String] + $Scope = "ANY", + + [Parameter(Mandatory = $false)] + [Boolean] + $Dedicated, + + [Parameter(Mandatory = $false)] + [Boolean] + $FromHome, + + [Parameter(Mandatory = $false)] + [Boolean] + $RequireHomeSite, + + [Parameter(Mandatory = $false)] + [Boolean] + $MultipleSessionAutoClean, + + [Parameter(Mandatory = $false)] + [Boolean] + $Enabled, + + [Parameter(Mandatory = $false)] + [ValidateSet('RDP', 'PCOIP', 'BLAST')] + [String[]] + $SupportedDisplayProtocols = @("PCOIP","BLAST"), + + [Parameter(Mandatory = $false)] + [ValidateSet("PCOIP",'RDP',"BLAST")] + [String] + $DefaultDisplayProtocol = 'PCOIP', + + [Parameter(Mandatory = $false)] + [Boolean] + $AllowUsersToChooseProtocol = $true, + + [Parameter(Mandatory = $false)] + [Boolean] + $AllowUsersToResetMachines = $false, + + [Parameter(Mandatory = $false)] + [Boolean] + $EnableHTMLAccess = $false, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $info = $services.PodFederation.PodFederation_get() + if ("ENABLED" -ne $info.localPodStatus.status) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys + if ($Type -eq 'DESKTOP_ENTITLEMENT') { + $GeService = New-Object VMware.HV.GlobalEntitlementService + $geBaseHelper = $GeService.getGlobalEntitlementBaseHelper() + $geBase = $geBaseHelper.getDataObject() + $geBase.Dedicated = $dedicated + $geBase.AllowUsersToResetMachines = $AllowUsersToResetMachines + } else { + $GeService = New-Object VMware.Hv.GlobalApplicationEntitlementService + $geBaseHelper = $GeService.getGlobalApplicationEntitlementBaseHelper() + $geBase = $geBaseHelper.getDataObject() + } + $geBase.DisplayName = $displayName + if ($description) { + $geBaseHelper.setDescription($Description) + } + $geBase.Scope = $Scope + $geBase.FromHome = $fromHome + $geBase.RequireHomeSite = $requireHomeSite + $geBase.MultipleSessionAutoClean = $multipleSessionAutoClean + $geBase.Enabled = $enabled + $geBase.DefaultDisplayProtocol = $defaultDisplayProtocol + $geBase.AllowUsersToChooseProtocol = $AllowUsersToChooseProtocol + $geBase.EnableHTMLAccess = $enableHTMLAccess + $geBase.SupportedDisplayProtocols = $supportedDisplayProtocols + Write-Host "Creating new global entitlement with DisplayName: " $DisplayName + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($displayName)) { + if ($type -eq 'DESKTOP_ENTITLEMENT') { + $GeService.GlobalEntitlement_Create($services, $geBase) + } else { + $GeService.GlobalApplicationEntitlement_Create($services, $geBase) + } + } + } + end { + [System.gc]::collect() + } + +} + + +function Find-HVGlobalEntitlement { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + $Param, + [Parameter(Mandatory = $true)] + [String] + $Type + ) + + # This translates the function arguments into the View API properties that must be queried + $GeSelectors = @{ + 'displayName' = 'base.displayName'; + 'description' = 'base.description'; + } + + $params = $Param + + $query_service_helper = New-Object VMware.Hv.QueryServiceService + $query = New-Object VMware.Hv.QueryDefinition + + $wildCard = $false + #Only supports wild card '*' + if ($params['displayName'] -and $params['displayName'].contains('*')) { + $wildcard = $true + } + + # build the query values + $query.queryEntityType = $Type + if (! $wildcard) { + [VMware.Hv.queryfilter[]]$filterSet = @() + foreach ($setting in $GeSelectors.Keys) { + if ($null -ne $params[$setting]) { + $equalsFilter = New-Object VMware.Hv.QueryFilterEquals + $equalsFilter.memberName = $GeSelectors[$setting] + $equalsFilter.value = $params[$setting] + $filterSet += $equalsFilter + } + } + if ($filterSet.Count -gt 0) { + $andFilter = New-Object VMware.Hv.QueryFilterAnd + $andFilter.Filters = $filterset + $query.Filter = $andFilter + } + $queryResults = $query_service_helper.QueryService_Query($services,$query) + $GeList = $queryResults.results + } + if ($wildcard -or [string]::IsNullOrEmpty($GeList)) { + $query.Filter = $null + $queryResults = $query_service_helper.QueryService_Query($services,$query) + $strFilterSet = @() + foreach ($setting in $GeSelectors.Keys) { + if ($null -ne $params[$setting]) { + if ($wildcard -and ($setting -eq 'displayName') ) { + $strFilterSet += '($_.' + $GeSelectors[$setting] + ' -like "' + $params[$setting] + '")' + } else { + $strFilterSet += '($_.' + $GeSelectors[$setting] + ' -eq "' + $params[$setting] + '")' + } + } + } + $whereClause = [string]::Join(' -and ', $strFilterSet) + $scriptBlock = [Scriptblock]::Create($whereClause) + $GeList = $queryResults.results | where $scriptBlock + } + Return $GeList +} + +function Get-HVGlobalEntitlement { + + <# +.Synopsis + Gets Global Entitlement(s) with given search parameters. + +.DESCRIPTION + Queries and returns global entitlement(s) and global application entitlement(s). + Global entitlements are used to route users to their resources across multiple pods. + +.PARAMETER DisplayName + Display Name of Global Entitlement. + +.PARAMETER Description + Description of Global Entitlement. + +.PARAMETER SuppressInfo + Suppress text info, when no global entitlement(s) found with given search parameters + +.PARAMETER HvServer + Reference to Horizon View Server. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + +.EXAMPLE + Get-HVGlobalEntitlement -DisplayName 'GEAPP' + Retrieves global application/desktop entitlement(s) with displayName 'GEAPP' + + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + +[CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [String] + $DisplayName, + + [Parameter(Mandatory = $false)] + [ValidateNotNullOrEmpty()] + [String] + $Description, + + [Parameter(Mandatory = $false)] + [boolean] + $SuppressInfo = $false, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $info = $services.PodFederation.PodFederation_get() + if ("ENABLED" -ne $info.localPodStatus.status) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + $result = @() + $result += Find-HVGlobalEntitlement -Param $psboundparameters -Type 'GlobalEntitlementSummaryView' + $result += Find-HVGlobalEntitlement -Param $psboundparameters -Type 'GlobalApplicationEntitlementInfo' + if (!$result -and !$SuppressInfo) { + Write-Host "Get-HVGlobalEntitlement: No global entitlement Found with given search parameters" + } + return $result + } + end { + [System.gc]::collect() + } +} + + +function Set-HVGlobalEntitlement { +<# +.SYNOPSIS + Sets the existing pool properties. + +.DESCRIPTION + This cmdlet allows user to edit global entitlements. + +.PARAMETER DisplayName + Display Name of Global Entitlement. + +.PARAMETER Description + Description of Global Entitlement. + +.PARAMETER EnableHTMLAccess + If set to true, the desktops that are associated with this GlobalEntitlement must also have HTML Access enabled. + +.PARAMETER Key + Property names path separated by . (dot) from the root of desktop spec. + +.PARAMETER Value + Property value corresponds to above key name. + +.PARAMETER HvServer + View API service object of Connect-HVServer cmdlet. + +.PARAMETER Spec + Path of the JSON specification file containing key/value pair. + +.EXAMPLE + Set-HVGlobalEntitlement -DisplayName 'MyGlobalEntitlement' -Spec 'C:\Edit-HVPool\EditPool.json' -Confirm:$false + Updates pool configuration by using json file + +.EXAMPLE + Set-HVGlobalEntitlement -DisplayName 'MyGlobalEntitlement' -Key 'base.description' -Value 'update description' + Updates pool configuration with given parameters key and value + +.EXAMPLE + Set-HVGlobalEntitlement -DisplayName 'MyGlobalEntitlement' -enableHTMLAccess $true + Set Allow HTML Access on a global entitlement. Note that it must also be enabled on the Pool and as of 7.3.0 Allow User to Choose Protocol must be enabled (which is unfortunately read-only) + +.EXAMPLE + Get-HVGlobalEntitlement | Set-HVGlobalEntitlement -Disable + Disable all global entitlements + +.OUTPUTS + None + +.NOTES + Author : Mark Elvers + Author email : mark.elvers@tunbury.org + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.3.0, 7.3.1 + PowerCLI Version : PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $true,ParameterSetName = 'option')] + [string] $displayName, + + [Parameter(ValueFromPipeline = $true,ParameterSetName = 'pipeline')] + $GlobalEntitlements, + + [Parameter(Mandatory = $false)] + [string]$Key, + + [Parameter(Mandatory = $false)] + $Value, + + [Parameter(Mandatory = $false)] + [string]$Spec, + + [Parameter(Mandatory = $false)] + [switch]$Enable, + + [Parameter(Mandatory = $false)] + [switch]$Disable, + + [Parameter(Mandatory = $false)] + [boolean]$enableHTMLAccess, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + + process { + $info = $services.PodFederation.PodFederation_get() + if ("ENABLED" -ne $info.localPodStatus.status) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys + $geList = @{} + if ($displayName) { + try { + $ge = Get-HVGlobalEntitlement -displayName $displayName -suppressInfo $true -hvServer $hvServer + } catch { + Write-Error "Make sure Get-HVGlobalEntitlement advanced function is loaded, $_" + break + } + if ($ge) { + $geList.add($ge.id, $ge.base.DisplayName) + } else { + Write-Error "No globalentitlement found with name: [$displayName]" + break + } + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $GlobalEntitlements) { + foreach ($item in $GlobalEntitlements) { + if ($item.GetType().name -eq 'GlobalEntitlementSummaryView') { + $geList.add($item.id, $item.Base.DisplayName) + } else { + Write-Error "In pipeline did not get object of expected type GlobalEntitlementSummaryView" + [System.gc]::collect() + return + } + } + } + + $updates = @() + if ($key -and $value) { + $updates += Get-MapEntry -key $key -value $value + } elseif ($key -or $value) { + Write-Error "Both key:[$key] and value:[$value] needs to be specified" + } + if ($spec) { + try { + $specObject = Get-JsonObject -specFile $spec + } catch { + Write-Error "Json file exception, $_" + return + } + foreach ($member in ($specObject.PSObject.Members | Where-Object { $_.MemberType -eq 'NoteProperty' })) { + $updates += Get-MapEntry -key $member.name -value $member.value + } + } + + if ($Enable) { + $updates += Get-MapEntry -key 'base.enabled' -value $true + } + elseif ($Disable) { + $updates += Get-MapEntry -key 'base.enabled' -value $false + } + + if ($PSBoundParameters.ContainsKey("enableHTMLAccess")) { + $updates += Get-MapEntry -key 'base.enableHTMLAccess' -value $enableHTMLAccess + } + + $ge_helper = New-Object VMware.HV.GlobalEntitlementService + foreach ($item in $geList.Keys) { + Write-Host "Updating the Entitlement: " $geList.$item + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($geList.$item)) { + $ge_helper.GlobalEntitlement_Update($services, $item, $updates) + } + } + } + + end { + [System.gc]::collect() + } +} + + +function Remove-HVGlobalEntitlement { + + <# +.Synopsis + Deletes a Global Entitlement. + +.DESCRIPTION + Deletes global entitlement(s) and global application entitlement(s). + Optionally, user can pipe the global entitlement(s) as input to this function. + +.PARAMETER DisplayName + Display Name of Global Entitlement. + +.PARAMETER HvServer + Reference to Horizon View Server. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Remove-HVGlobalEntitlement -DisplayName 'GE_APP' + Deletes global application/desktop entitlement with displayName 'GE_APP' + +.EXAMPLE + Get-HVGlobalEntitlement -DisplayName 'GE_*' | Remove-HVGlobalEntitlement + Deletes global application/desktop entitlement(s), if displayName matches with 'GE_*' + + +.NOTES + Author : Praveen Mathamsetty. + Author email : pmathamsetty@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.1.0 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + +[CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true, ParameterSetName = 'Default')] + [ValidateNotNullOrEmpty()] + [String] + $DisplayName, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'pipeline')] + $GlobalEntitlement, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + process { + $info = $services.PodFederation.PodFederation_get() + if ("ENABLED" -ne $info.localPodStatus.status) { + Write-Host "Multi-DataCenter-View/CPA is not enabled" + return + } + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys + $GeList = @() + if ($DisplayName) { + try { + $GeList = Get-HVGlobalEntitlement -DisplayName $DisplayName -suppressInfo $true -hvServer $hvServer + } catch { + Write-Error "Make sure Get-HVGlobalEntitlement advanced function is loaded, $_" + break + } + } elseif ($PSCmdlet.MyInvocation.ExpectingInput -or $GlobalEntitlement) { + foreach ($item in $GlobalEntitlement) { + if (($item.GetType().name -ne 'GlobalEntitlementSummaryView') -and ($item.GetType().name -ne 'GlobalApplicationEntitlementInfo')) { + Write-Error "In pipeline did not get object of expected type GlobalApplicationEntitlementInfo/GlobalEntitlementSummaryView" + [System.gc]::collect() + return + } + $GeList += ,$item + } + } + foreach ($item in $GeList) { + Write-Host "Deleting global entitlement with DisplayName: " $item.base.displayName + if (!$confirmFlag -OR $pscmdlet.ShouldProcess($item.base.displayName)) { + if ($item.GetType().Name -eq 'GlobalEntitlementSummaryView') { + $services.GlobalEntitlement.GlobalEntitlement_Delete($item.id) + } else { + $services.GlobalApplicationEntitlement.GlobalApplicationEntitlement_Delete($item.id) + } + } + } + } + end { + [System.gc]::collect() + } + +} + +function Get-HVGlobalSession { +<# +.SYNOPSIS +Provides a list with all Global sessions in a Cloud Pod Architecture + +.DESCRIPTION +The get-hvglobalsession gets all local session by using view API service object(hvServer) of Connect-HVServer cmdlet. + +.PARAMETER HvServer + View API service object of Connect-HVServer cmdlet. + +.EXAMPLE + Get-hvglobalsession + Gets all global sessions + +.NOTES + Author : Wouter Kursten. + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.1.0, 7.3.2 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + +#> +[CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' +)] + +param( + [Parameter(Mandatory = $false)] + $HvServer = $null +) + +$services = Get-ViewAPIService -HvServer $HvServer +if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object." + break +} + +$query_service_helper = New-Object VMware.Hv.GlobalSessionQueryServiceService +$query=new-object vmware.hv.GlobalSessionQueryServiceQuerySpec + +$SessionList = @() +foreach ($pod in $services.Pod.Pod_List()) { + $query.pod=$pod.id + $queryResults = $query_service_helper.GlobalSessionQueryService_QueryWithSpec($services, $query) + $GetNext = $false + do { + if ($GetNext) { $queryResults = $query_service_helper.GlobalSessionQueryService_GetNext($services, $queryResults.id) } + $SessionList += $queryResults.results + $GetNext = $true + } while ($queryResults.remainingCount -gt 0) + $query_service_helper.GlobalSessionQueryService_Delete($services, $queryresults.id) + +} +return $sessionlist +} + +function Set-HVApplicationIcon { +<# +.SYNOPSIS + Used to create/update an icon association for a given application. + +.DESCRIPTION + This function is used to create an application icon and associate it with the given application. If the specified icon already exists in the LDAP, it will just updates the icon association to the application. Any of the existing customized icon association to the given application will be overwritten. + +.PARAMETER ApplicationName + Name of the application to which the association to be made. + +.PARAMETER IconPath + Path of the icon. + +.PARAMETER HvServer + View API service object of Connect-HVServer cmdlet. + +.EXAMPLE + Creating the icon I1 and associating with application A1. Same command is used for update icon also. + Set-HVApplicationIcon -ApplicationName A1 -IconPath C:\I1.ico -HvServer $hvServer + +.OUTPUTS + None + +.NOTES + Author : Paramesh Oddepally. + Author email : poddepally@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.1 + PowerCLI Version : PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $true)] + [string] $ApplicationName, + + [Parameter(Mandatory = $true)] + $IconPath, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + begin { + $services = Get-ViewAPIService -HvServer $HvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object." + break + } + Add-Type -AssemblyName System.Drawing + } + + process { + try { + $appInfo = Get-HVQueryResult -EntityType ApplicationInfo -Filter (Get-HVQueryFilter data.name -Eq $ApplicationName) -HvServer $HvServer + } catch { + # EntityNotFound, InsufficientPermission, InvalidArgument, InvalidType, UnexpectedFault + Write-Error "Error in querying the ApplicationInfo for Application:[$ApplicationName] $_" + break + } + + if ($null -eq $appInfo) { + Write-Error "No application found with specified name:[$ApplicationName]." + break + } + + if (!(Test-Path $IconPath)) { + Write-Error "File:[$IconPath] does not exists" + break + } + + $spec = New-Object VMware.Hv.ApplicationIconSpec + $base = New-Object VMware.Hv.ApplicationIconBase + + try { + $fileHash = Get-FileHash -Path $IconPath -Algorithm MD5 + $base.IconHash = $fileHash.Hash + $base.Data = (Get-Content $iconPath -Encoding byte) + $bitMap = [System.Drawing.Bitmap]::FromFile($iconPath) + $base.Width = $bitMap.Width + $base.Height = $bitMap.Height + $base.IconSource = "broker" + $base.Applications = @($appInfo.Id) + $spec.ExecutionData = $base + } catch { + Write-Error "Error in reading the icon parameters: $_" + break + } + + if ($base.Height -gt 256 -or $base.Width -gt 256) { + Write-Error "Invalid image resolution. Maximum resolution for an icon should be 256*256." + break + } + + $ApplicationIconHelper = New-Object VMware.Hv.ApplicationIconService + try { + $ApplicationIconId = $ApplicationIconHelper.ApplicationIcon_CreateAndAssociate($services, $spec) + } catch { + if ($_.Exception.InnerException.MethodFault.GetType().name.Equals('EntityAlreadyExists')) { + # This icon is already part of LDAP and associated with some other application(s). + # In this case, call updateAssociations + $applicationIconId = $_.Exception.InnerException.MethodFault.Id + Write-Host "Some application(s) already have an association for the specified icon." + $ApplicationIconHelper.ApplicationIcon_UpdateAssociations($services, $applicationIconId, @($appInfo.Id)) + Write-Host "Successfully updated customized icon association for Application:[$ApplicationName]." + break + } + Write-Host "Error in associating customized icon for Application:[$ApplicationName] $_" + break + } + Write-Host "Successfully associated customized icon for Application:[$ApplicationName]." + } + + end { + [System.gc]::collect() + } +} + +Function Remove-HVApplicationIcon { +<# +.SYNOPSIS + Used to remove a customized icon association for a given application. + +.DESCRIPTION + This function is used to remove an application association to the given application. It will never remove the RDS system icons. If application doesnot have any customized icon, an error will be thrown. + +.PARAMETER ApplicationName + Name of the application to which customized icon needs to be removed. + +.PARAMETER HvServer + View API service object of Connect-HVServer cmdlet. + +.EXAMPLE + Removing the icon for an application A1. + Remove-HVApplicationIcon -ApplicationName A1 -HvServer $hvServer + +.OUTPUTS + None + +.NOTES + Author : Paramesh Oddepally. + Author email : poddepally@vmware.com + Version : 1.1 + + ===Tested Against Environment==== + Horizon View Server Version : 7.1 + PowerCLI Version : PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + param( + [Parameter(Mandatory = $true)] + [string] $ApplicationName, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + begin { + $services = Get-ViewAPIService -HvServer $HvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object." + break + } + } + + process { + try { + $appInfo = Get-HVQueryResult -EntityType ApplicationInfo -Filter (Get-HVQueryFilter data.name -Eq $ApplicationName) -HvServer $HvServer + } catch { + # EntityNotFound, InsufficientPermission, InvalidArgument, InvalidType, UnexpectedFault + Write-Error "Error in querying the ApplicationInfo for Application:[$ApplicationName] $_" + break + } + + if ($null -eq $appInfo) { + Write-Error "No application found with specified name:[$ApplicationName]" + break + } + + [VMware.Hv.ApplicationIconId[]] $icons = $appInfo.Icons + [VMware.Hv.ApplicationIconId] $brokerIcon = $null + $ApplicationIconHelper = New-Object VMware.Hv.ApplicationIconService + Foreach ($icon in $icons) { + $applicationIconInfo = $ApplicationIconHelper.ApplicationIcon_Get($services, $icon) + if ($applicationIconInfo.Base.IconSource -eq "broker") { + $brokerIcon = $icon + } + } + + if ($null -eq $brokerIcon) { + Write-Error "There is no customized icon for the Application:[$ApplicationName]." + break + } + + try { + $ApplicationIconHelper.ApplicationIcon_RemoveAssociations($services, $brokerIcon, @($appInfo.Id)) + } catch { + Write-Error "Error in removing the customized icon association for Application:[$ApplicationName] $_ " + break + } + Write-Host "Successfully removed customized icon association for Application:[$ApplicationName]." + } + + end { + [System.gc]::collect() + } +} + +function Get-HVGlobalSettings { +<# +.Synopsis + Gets a list of Global Settings + +.DESCRIPTION + Queries and returns the Global Settings for the pod of the specified HVServer. + +.PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered inplace of hvServer + +.EXAMPLE + Get-HVGlobalSettings + +.OUTPUTS + Returns list of object type VMware.Hv.GlobalSettingsInfo + +.NOTES + Author : Matt Frey. + Author email : mfrey@vmware.com + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.1 + PowerCLI Version : PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + begin { + $services = Get-ViewAPIService -hvServer $hvServer + + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + + process { + + $globalSettings = $services.GlobalSettings.GlobalSettings_Get() + + } + + end { + + Return $globalSettings + + } +} + +function Set-HVGlobalSettings { +<# +.SYNOPSIS + Sets the Global Settings of the Connection Server Pod + +.DESCRIPTION + This cmdlet allows user to set Global Settings by passing key/value pair or by passing specific parameters. Optionally, user can pass a JSON spec file. + +.PARAMETER Key + Property names path separated by . (dot) from the root of global settings spec. + +.PARAMETER Value + Property value corresponds to above key name. + +.PARAMETER HvServer + View API service object of Connect-HVServer cmdlet. + +.PARAMETER Spec + Path of the JSON specification file containing key/value pair. + +.PARAMETER clientMaxSessionTimePolicy + Client max session lifetime policy. + "TIMEOUT_AFTER" Indicates that the client session times out after a configurable session length (in minutes) + "NEVER" Indicates no absolute client session length (sessions only end due to inactivity) + +.PARAMETER clientMaxSessionTimeMinutes + Determines how long a user can keep a session open after logging in to View Connection Server. The value is set in minutes. When a session times out, the session is terminated and the View client is disconnected from the resource. + Default value is 600. + Minimum value is 5. + Maximum value is 600. + This property is required if clientMaxSessionTimePolicy is set to "TIMEOUT_AFTER" + +.PARAMETER clientIdleSessionTimeoutPolicy + Specifies the policy for the maximum time that a that a user can be idle before the broker takes measure to protect the session. + "TIMEOUT_AFTER" Indicates that the user session can be idle for a configurable max time (in minutes) before the broker takes measure to protect the session. + "NEVER" Indicates that the client session is never locked. + +.PARAMETER clientIdleSessionTimeoutMinutes + Determines how long a that a user can be idle before the broker takes measure to protect the session. The value is set in minutes. + Default value is 15 + This property is required if -clientIdleSessionTimeoutPolicy is set to "TIMEOUT_AFTER" + +.PARAMETER clientSessionTimeoutMinutes + Determines the maximum length of time that a Broker session will be kept active if there is no traffic between a client and the Broker. The value is set in minutes. + Default value is 1200 + Minimum value is 5 + +.PARAMETER desktopSSOTimeoutPolicy + The single sign on setting for when a user connects to View Connection Server. + "DISABLE_AFTER" SSO is disabled the specified number of minutes after a user connects to View Connection Server. + "DISABLED" Single sign on is always disabled. + "ALWAYS_ENABLED" Single sign on is always enabled. + +.PARAMETER desktopSSOTimeoutMinutes + SSO is disabled the specified number of minutes after a user connects to View Connection Server. + Minimum value is 1 + Maximum value is 999 + +.PARAMETER applicationSSOTimeoutPolicy + The single sign on timeout policy for application sessions. + "DISABLE_AFTER" SSO is disabled the specified number of minutes after a user connects to View Connection Server. + "DISABLED" Single sign on is always disabled. + "ALWAYS_ENABLED" Single sign on is always enabled. + +.PARAMETER applicationSSOTimeoutMinutes + SSO is disabled the specified number of minutes after a user connects to View Connection Server. + Minimum value is 1 + Maximum value is 999 + +.PARAMETER viewAPISessionTimeoutMinutes + Determines how long (in minutes) an idle View API session continues before the session times out. Setting the View API session timeout to a high number of minutes increases the risk of unauthorized use of View API. Use caution when you allow an idle session to persist a long time. + Default value is 10 + Minimum value is 1 + Maximum value is 4320 + +.PARAMETER preLoginMessage + Displays a disclaimer or another message to View Client users when they log in. No message will be displayed if this is null. + +.PARAMETER displayWarningBeforeForcedLogoff + Displays a warning message when users are forced to log off because a scheduled or immediate update such as a machine-refresh operation is about to start. + $TRUE or $FALSE + +.PARAMETER forcedLogoffMinutes + The number of minutes to wait after the warning is displayed and before logging off the user. + Default value is 5 + Minimum value is 1 + Maximum value is 999999 + This property is required if displayWarningBeforeForcedLogoff is $true + +.PARAMETER forcedLogoffMessage + The warning to be displayed before logging off the user. + +.PARAMETER enableServerInSingleUserMode + Permits certain RDSServer operating systems to be used for non-RDS Desktops. + +.PARAMETER storeCALOnBroker + Used for configuring whether or not to store the RDS Per Device CAL on Broker. + $TRUE or $FALSE + +.PARAMETER storeCALOnClient + Used for configuring whether or not to store the RDS Per Device CAL on client devices. This value can be true only if the storeCALOnBroker is true. + $TRUE or $FALSE + +.PARAMETER reauthSecureTunnelAfterInterruption + Reauthenticate secure tunnel connections after network interruption Determines if user credentials must be reauthenticated after a network interruption when View clients use secure tunnel connections to View resources. When you select this setting, if a secure tunnel connection ends during a session, View Client requires the user to reauthenticate before reconnecting. This setting offers increased security. For example, if a laptop is stolen and moved to a different network, the user cannot automatically gain access to the remote resource because the network connection was temporarily interrupted. When this setting is not selected, the client reconnects to the resource without requiring the user to reauthenticate. This setting has no effect when you use direct connection. + +.PARAMETER messageSecurityMode + Determines if signing and verification of the JMS messages passed between View Manager components takes place. + "DISABLED" Message security mode is disabled. + "MIXED" Message security mode is enabled but not enforced. You can use this mode to detect components in your View environment that predate View Manager 3.0. The log files generated by View Connection Server contain references to these components. + "ENABLED" Message security mode is enabled. Unsigned messages are rejected by View components. Message security mode is enabled by default. Note: View components that predate View Manager 3.0 are not allowed to communicate with other View components. + "ENHANCED" Message Security mode is Enhanced. Message signing and validation is performed based on the current Security Level and desktop Message Security mode. + +.PARAMETER enableIPSecForSecurityServerPairing + Determines whether to use Internet Protocol Security (IPSec) for connections between security servers and View Connection Server instances. By default, secure connections (using IPSec) for security server connections is enabled. + $TRUE or $FALSE + +.EXAMPLE + Set-HVGlobalSettings 'ManualPool' -Spec 'C:\Set-HVGlobalSettings\Set-GlobalSettings.json' + +.EXAMPLE + Set-HVGlobalSettings -Key 'generalData.clientMaxSessionTimePolicy' -Value 'NEVER' + +.EXAMPLE + Set-HVGlobalSettings -clientMaxSessionTimePolicy "TIMEOUT_AFTER" -clientMaxSessionTimeMinutes 1200 + +.OUTPUTS + None + +.NOTES + Author : Matt Frey. + Author email : mfrey@vmware.com + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.1 + PowerCLI Version : PowerCLI 6.5.1 + PowerShell Version : 5.0 +#> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $false)] + [string]$Key, + + [Parameter(Mandatory = $false)] + $Value, + + [Parameter(Mandatory = $false)] + [string]$Spec, + + [Parameter(Mandatory = $false)] + [ValidateSet('TIMEOUT_AFTER','NEVER')] + [string]$clientMaxSessionTimePolicy, + + [Parameter(Mandatory = $false)] + [ValidateRange(5,600)] + [Int]$clientMaxSessionTimeMinutes, + + [Parameter(Mandatory = $false)] + [ValidateSet('TIMEOUT_AFTER','NEVER')] + [string]$clientIdleSessionTimeoutPolicy, + + [Parameter(Mandatory = $false)] + [Int]$clientIdleSessionTimeoutMinutes, + + [Parameter(Mandatory = $false)] + [Int]$clientSessionTimeoutMinutes, + + [Parameter(Mandatory = $false)] + [ValidateSet('DISABLE_AFTER','DISABLED','ALWAYS_ENABLED')] + [string]$desktopSSOTimeoutPolicy, + + [Parameter(Mandatory = $false)] + [ValidateRange(1,999)] + [Int]$desktopSSOTimeoutMinutes, + + [Parameter(Mandatory = $false)] + [ValidateSet('DISABLE_AFTER','DISABLED','ALWAYS_ENABLED')] + [string]$applicationSSOTimeoutPolicy, + + [Parameter(Mandatory = $false)] + [ValidateRange(1,999)] + [Int]$applicationSSOTimeoutMinutes, + + [Parameter(Mandatory = $false)] + [ValidateRange(1,4320)] + [Int]$viewAPISessionTimeoutMinutes, + + [Parameter(Mandatory = $false)] + [string]$preLoginMessage, + + [Parameter(Mandatory = $false)] + [boolean]$displayWarningBeforeForcedLogoff, + + [Parameter(Mandatory = $false)] + [ValidateRange(1,999999)] + [Int]$forcedLogoffTimeoutMinutes, + + [Parameter(Mandatory = $false)] + [string]$forcedLogoffMessage, + + [Parameter(Mandatory = $false)] + [boolean]$enableServerInSingleUserMode, + + [Parameter(Mandatory = $false)] + [boolean]$storeCALOnBroker, + + [Parameter(Mandatory = $false)] + [boolean]$storeCALOnClient, + + [Parameter(Mandatory = $false)] + [boolean]$reauthSecureTunnelAfterInterruption, + + [Parameter(Mandatory = $false)] + [ValidateSet('DISABLED','MIXED','ENABLED','ENHANCED')] + [string]$messageSecurityMode, + + [Parameter(Mandatory = $false)] + [boolean]$enableIPSecForSecurityServerPairing, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + begin { + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + + process { + + $updates = @() + if ($key -and $value) { + $updates += Get-MapEntry -key $key -value $value + } elseif ($key -or $value) { + Write-Error "Both key:[$key] and value:[$value] needs to be specified" + } + if ($spec) { + try { + $specObject = Get-JsonObject -specFile $spec + } catch { + Write-Error "Json file exception, $_" + return + } + foreach ($member in ($specObject.PSObject.Members | Where-Object { $_.MemberType -eq 'NoteProperty' })) { + $updates += Get-MapEntry -key $member.name -value $member.value + } + } + if ($clientMaxSessionTimePolicy) { + $updates += Get-MapEntry -key 'generalData.clientMaxSessionTimePolicy' -Value $clientMaxSessionTimePolicy + } + if ($clientMaxSessionTimeMinutes) { + $updates += Get-MapEntry -key 'generalData.clientMaxSessionTimeMinutes' -Value $clientMaxSessionTimeMinutes + } + if ($clientIdleSessionTimeoutPolicy) { + $updates += Get-MapEntry -key 'generalData.clientIdleSessionTimeoutPolicy' -Value $clientIdleSessionTimeoutPolicy + } + if ($clientIdleSessionTimeoutMinutes) { + $updates += Get-MapEntry -key 'generalData.clientIdleSessionTimeoutMinutes' -Value $clientIdleSessionTimeoutMinutes + } + if ($clientSessionTimeoutMinutes) { + $updates += Get-MapEntry -key 'generalData.clientSessionTimeoutMinutes' -Value $clientSessionTimeoutMinutes + } + if ($desktopSSOTimeoutPolicy) { + $updates += Get-MapEntry -key 'generalData.desktopSSOTimeoutPolicy' -Value $desktopSSOTimeoutPolicy + } + if ($desktopSSOTimeoutMinutes) { + $updates += Get-MapEntry -key 'generalData.desktopSSOTimeoutMinutes' -Value $desktopSSOTimeoutMinutes + } + if ($applicationSSOTimeoutPolicy) { + $updates += Get-MapEntry -key 'generalData.applicationSSOTimeoutPolicy' -Value $applicationSSOTimeoutPolicy + } + if ($applicationSSOTimeoutMinutes) { + $updates += Get-MapEntry -key 'generalData.applicationSSOTimeoutMinutes' -Value $applicationSSOTimeoutMinutes + } + if ($viewAPISessionTimeoutMinutes) { + $updates += Get-MapEntry -key 'generalData.viewAPISessionTimeoutMinutes' -Value $viewAPISessionTimeoutMinutes + } + if ($preLoginMessage) { + $updates += Get-MapEntry -key 'generalData.preLoginMessage' -Value $preLoginMessage + } + if ($displayWarningBeforeForcedLogoff) { + $updates += Get-MapEntry -key 'generalData.displayWarningBeforeForcedLogoff' -Value $displayWarningBeforeForcedLogoff + } + if ($forcedLogoffTimeoutMinutes) { + $updates += Get-MapEntry -key 'generalData.forcedLogoffTimeoutMinutes' -Value $forcedLogoffTimeoutMinutes + } + if ($forcedLogoffMessage) { + $updates += Get-MapEntry -key 'generalData.forcedLogoffMessage' -Value $forcedLogoffMessage + } + if ($enableServerInSingleUserMode) { + $updates += Get-MapEntry -key 'generalData.enableServerInSingleUserMode' -Value $enableServerInSingleUserMode + } + if ($storeCALOnBroker) { + $updates += Get-MapEntry -key 'generalData.storeCALOnBroker' -Value $storeCALOnBroker + } + if ($storeCALOnClient) { + $updates += Get-MapEntry -key 'generalData.storeCALOnClient' -Value $storeCALOnClient + } + if ($reauthSecureTunnelAfterInterruption) { + $updates += Get-MapEntry -key 'securityData.reauthSecureTunnelAfterInterruption' -Value $reauthSecureTunnelAfterInterruption + } + if ($messageSecurityMode) { + $updates += Get-MapEntry -key 'securityData.messageSecurityMode' -Value $messageSecurityMode + } + if ($enableIPSecForSecurityServerPairing) { + $updates += Get-MapEntry -key 'securityData.enableIPSecForSecurityServerPairing' -Value $enableIPSecForSecurityServerPairing + } + + $global_settings_helper = New-Object VMware.Hv.GlobalSettingsService + + $global_settings_helper.GlobalSettings_Update($services,$updates) + + } + + end { + [System.gc]::collect() + } +} + +function get-HVlocalsession { +<# +.SYNOPSIS +Provides a list with all sessions on the local pod (works in CPA and non-CPA) + +.DESCRIPTION +The get-hvlocalsession gets all local session by using view API service object(hvServer) of Connect-HVServer cmdlet. + +.PARAMETER HvServer + View API service object of Connect-HVServer cmdlet. + +.EXAMPLE + Get-hvlocalsession + Get all local sessions + +.NOTES + Author : Wouter Kursten. + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.0.2, 7.1.0, 7.3.2 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + +#> + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + $services = Get-ViewAPIService -HvServer $HvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object." + break + } + + $query_service_helper = New-Object VMware.Hv.QueryServiceService + $query = New-Object VMware.Hv.QueryDefinition + + $query.queryEntityType = 'SessionLocalSummaryView' + $SessionList = @() + $GetNext = $false + $queryResults = $query_service_helper.QueryService_Create($services, $query) + do { + if ($GetNext) { $queryResults = $query_service_helper.QueryService_GetNext($services, $queryResults.id) } + $SessionList += $queryResults.results + $GetNext = $true + } + while ($queryResults.remainingCount -gt 0) + $query_service_helper.QueryService_Delete($services, $queryResults.id) + + + return $sessionlist + [System.gc]::collect() +} + +function Reset-HVMachine { + <# + .Synopsis + Resets Horizon View desktops. + + .DESCRIPTION + Queries and resets virtual machines, the machines list would be determined + based on queryable fields machineName. Use an asterisk (*) as wildcard. If the result has multiple machines all will be reset. + Please note that on an Instant Clone Pool this will do the same as a recover of the machine. + + .PARAMETER MachineName + The name of the Machine(s) to query for. + This is a required value. + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + reset-HVMachine -MachineName 'PowerCLIVM' + Queries VM(s) with given parameter machineName + + + .EXAMPLE + reset-HVMachine -MachineName 'PowerCLIVM*' + Queries VM(s) with given parameter machinename with wildcard character * + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.3.2 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + + [Parameter(Mandatory = $true)] + [string] + $MachineName, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + + $machineList = Find-HVMachine -Param $PSBoundParameters + if (!$machineList) { + Write-Host "Reset-HVMachine: No Virtual Machine(s) Found with given search parameters" + break + } + foreach ($machine in $machinelist){ + $services.machine.Machine_ResetMachines($machine.id) + } +} +function Remove-HVMachine { + <# + .Synopsis + Remove a Horizon View desktop or desktops. + + .DESCRIPTION + Deletes a VM or an array of VM's from Horizon. Utilizes an Or query filter to match machine names. + + .PARAMETER HVServer + The Horizon server where the machine to be deleted resides. Parameter is not mandatory, + but if you do not specify the server, than make sure you are connected to a Horizon server + first with connect-hvserver. + + .PARAMETER MachineNames + The name or names of the machine(s) to be deleted. Accepts a single VM or an array of VM names.This is a mandatory parameter. + + .PARAMETER DeleteFromDisk + Determines whether the Machine VM should be deleted from vCenter Server. This is only applicable for managed machines. + This must always be true for machines in linked and instant clone desktops. + This defaults to true for linked and instant clone machines and false for all other types. + + .EXAMPLE + Remove-HVMachine -HVServer 'horizonserver123' -MachineNames 'LAX-WIN10-002' + Deletes VM 'LAX-WIN10-002' from HV Server 'horizonserver123' + + .EXAMPLE + Remove-HVMachine -HVServer 'horizonserver123' -MachineNames $machines + Deletes VM's contained within an array of machine names from HV Server 'horizonserver123' + + .EXAMPLE + Remove-HVMachine -HVServer 'horizonserver123' -MachineNames 'ManualVM01' -DeleteFromDisk:$false + Deletes VM 'ManualVM01' from Horizon inventory, but not from vSphere. Note this only works for Full Clone VMs. + + .NOTES + Author : Jose Rodriguez + Author email : jrodsguitar@gmail.com + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.1.1 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + + [Parameter(Mandatory = $true)] + [array] + $MachineNames, + + [Parameter(Mandatory = $false)] + [switch]$DeleteFromDisk = $true, + + [Parameter(Mandatory = $false)] + $HVServer = $null + ) + +#Connect to HV Server +$services = Get-ViewAPIService -HVServer $HVServer + +if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + +#Connect to Query Service +$queryService = New-Object 'Vmware.Hv.QueryServiceService' +#QUery Definition +$queryDefinition = New-Object 'Vmware.Hv.QueryDefinition' +#Query Filter +$queryDefinition.queryEntityType = 'MachineNamesView' + +#Create Filter Set so we can populate it with QueryFilterEquals data +[VMware.Hv.queryfilter[]]$filterSet = @() +foreach($machine in $machineNames){ + + #queryfilter values + $queryFilterEquals = New-Object VMware.Hv.QueryFilterEquals + $queryFilterEquals.memberName = "base.name" + $queryFilterEquals.value = "$machine" + + $filterSet += $queryFilterEquals + +} + +#Or Filter +$orFilter = New-Object VMware.Hv.QueryFilterOr +$orFilter.filters = $filterSet + +#Set Definition filter to value of $orfilter +$queryDefinition.filter = $orFilter + +#Retrieve query results. Returns all machines to be deleted +$queryResults = $queryService.QueryService_Query($services,$queryDefinition) + +#Assign VM Object to variable +$deleteThisMachine = $queryResults.Results + +#Machine Service +$machineService = new-object VMware.Hv.MachineService + +#Get Machine Service machine object +$deleteMachine = $machineService.Machine_GetInfos($services,$deleteThisMachine.Id) + +#If sessions exist on the machines we are going to delete than force kill those sessions. +#The deleteMachines method will not work if there are any existing sessions so this step is very important. +write-host "Attemtping log off of machines" + +if($deleteMachine.base.session.id){ +$trys = 0 + + do{ + foreach($session in $deleteMachine.base.session){ + + $sessions = $null + [VMware.Hv.SessionId[]]$sessions += $session + + } + + try{ + + write-host "`n" + write-host "Attemtping log off of machines" + write-host "`n" + $logOffSession = new-object 'VMware.Hv.SessionService' + $logOffSession.Session_LogoffSessionsForced($services,$sessions) + + #Wait more for Sessions to end + + Start-Sleep -Seconds 5 + + } + + catch{ + + Write-Host "Attempted to Log Off Sessions from below machines but recieved an error. This doesn't usually mean it failed. Typically the session is succesfully logged off but takes some time" + write-host "`n" + write-host ($deleteMachine.base.Name -join "`n") + + start-sleep -seconds 5 + + } + + if(($trys -le 10)){ + + write-host "`n" + write-host "Retrying Logoffs: $trys times" + #Recheck existing sessions + $deleteMachine = $machineService.Machine_GetInfos($services,$deleteThisMachine.Id) + + } + + $trys++ + + } + + until((!$deleteMachine.base.session.id) -or ($trys -gt 10)) + +} + +#Create delete spec for the DeleteMachines method +$deleteSpec = [VMware.Hv.MachineDeleteSpec]::new() +$deleteSpec.DeleteFromDisk = $DeleteFromDisk +$deleteSpec.ArchivePersistentDisk = $false + +#Delete the machines +write-host "Attempting to Delete:" +Write-Output ($deleteMachine.base.Name -join "`n") +$bye = $machineService.Machine_DeleteMachines($services,$deleteMachine.id,$deleteSpec) + +[System.gc]::collect() + +} + +function get-hvhealth { + <# + .Synopsis + Pulls health information from Horizon View + + .DESCRIPTION + Queries and returns health information from the local Horizon Pod + + .PARAMETER Servicename + The name of the service to query the health for. + This will default to Connection server health. + Available services are ADDomain,CertificateSSOConnector,ConnectionServer,EventDatabase,SAMLAuthenticator,SecurityServer,ViewComposer,VirtualCenter,Pod + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + get-hvhealth -service connectionserver + Returns health for the connectionserver(s) + + + .EXAMPLE + get-hvhealth -service ViewComposer + Returns health for the View composer server(s) + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.3.2,7.4 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High' + )] + + param( + + [Parameter(Mandatory = $false)] + [ValidateSet('ADDomain', 'CertificateSSOConnector', 'ConnectionServer', 'EventDatabase', 'SAMLAuthenticator', 'SecurityServer', 'ViewComposer', 'VirtualCenter', 'pod')] + [string] + $Servicename = 'ConnectionServer', + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + + switch ($Servicename) { + 'ADDomain' { + $healthinfo=$services.ADDomainHealth.ADDomainHealth_List() + } + 'CertificateSSOConnector' { + $healthinfo=$services.CertificateSSOConnectorHealth.CertificateSSOConnectorHealth_list() + } + 'ConnectionServer' { + $healthinfo=$services.ConnectionServerHealth.ConnectionServerHealth_list() + } + 'EventDatabase' { + $healthinfo=$services.EventDatabaseHealth.EventDatabaseHealth_Get() + } + 'SAMLAuthenticator' { + $healthinfo=$services.SAMLAuthenticatorHealth.SAMLAuthenticatorHealth_List() + } + 'SecurityServer' { + $healthinfo=$services.SecurityServerHealth.SecurityServerHealth_List() + } + 'ViewComposer' { + $healthinfo=$services.ViewComposerHealth.ViewComposerHealth_List() + } + 'VirtualCenter' { + $healthinfo=$services.VirtualCenterHealth.VirtualCenterHealth_List() + } + 'Pod' { + $healthinfo=$services.podhealth.PodHealth_List() + } + } + if ($healthinfo){ + return $healthinfo + } + else { + Write-Output "No healthdata found for the $servicename service" + } + [System.gc]::collect() +} + +function new-hvpodfederation { + <# + .Synopsis + Initiates a Horizon View Pod Federation (Cloud Pod Architecture) + + .DESCRIPTION + Starts the initialisation of a Horizon View Pod Federation. Other pod's can be joined to this federation to form the Cloud Pod Architecture + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + new-hvpodfederation + Returns health for the connectionserver(s) + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.3.2,7.4 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + $services.PodFederation.PodFederation_Initialize() + + Write-Output "The Pod Federation has been initiated. Please wait a couple of minutes and refresh any open admin consoles to use the newly available functionality." + + [System.gc]::collect() +} + +function remove-hvpodfederation { + <# + .Synopsis + Uninitiates a Horizon View Pod Federation (Cloud Pod Architecture) + + .DESCRIPTION + Starts the uninitialisation of a Horizon View Pod Federation. It does NOT remove a pod from a federation. + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + Starts the Uninitiates a Horizon View Pod Federation. + Unintialises + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.3.2,7.4 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + $services.PodFederation.PodFederation_Uninitialize() + + Write-Output "The uninitialisation of the Pod Federation has been started. Please wait a couple of minutes and refresh any open admin consoles to see the results." + + [System.gc]::collect() +} + +function get-hvpodfederation { + <# + .Synopsis + Returns information about a Horizon View Pod Federation (Cloud Pod Architecture) + + .DESCRIPTION + Returns information about a Horizon View Pod Federation (Cloud Pod Architecture) + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + get-hvpodfederation + Returns information about a Horizon View Pod Federation + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.3.2,7.4 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + $podfederationinfo=$services.PodFederation.PodFederation_Get() + return $podfederationinfo + + [System.gc]::collect() +} + +function register-hvpod { + <# + .Synopsis + Registers a pod in a Horizon View Pod Federation (Cloud Pod Architecture) + + .DESCRIPTION + Registers a pod in a Horizon View Pod Federation. You have to be connected to the pod you are joining to the federation. + + .PARAMETER ADUserName + User principal name of user this is required to be in the domain\username format + + .PARAMETER remoteconnectionserver + Servername of a connectionserver that already belongs to the PodFederation + + .PARAMETER ADPassword + Password of the type Securestring. Can be created with: + $password = Read-Host 'Domain Password' -AsSecureString + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + C:\PS>$adpassword = Read-Host 'Domain Password' -AsSecureString + C:\PS>register-hvpod -remoteconnectionserver "servername" -username "user\domain" -password $adpassword + + .EXAMPLE + register-hvpod -remoteconnectionserver "servername" -username "user\domain" + It will now ask for the password + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.3.2,7.4 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $true)] + [String] + $remoteconnectionserver, + + [Parameter(Mandatory = $true)] + [ValidatePattern("^.+?[@\\].+?$")] + [String] + $ADUserName, + + [Parameter(Mandatory = $true)] + [securestring] + $ADpassword, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + + #if ($ADPassword -eq $null) { + #$ADPassword= Read-Host 'Please provide the Active Directory password for user $AdUsername' -AsSecureString + #} + + $temppw = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($ADPassword) + $PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($temppw) + $vcPassword = New-Object VMware.Hv.SecureString + $enc = [system.Text.Encoding]::UTF8 + $vcPassword.Utf8String = $enc.GetBytes($PlainPassword) + + $services.PodFederation.PodFederation_join($remoteconnectionserver,$adusername,$vcpassword) + write-host "This pod has been joined to the podfederation." + + [System.gc]::collect() +} + +function unregister-hvpod { + <# + .Synopsis + Removes a pod from a podfederation + + .DESCRIPTION + Starts the uninitialisation of a Horizon View Pod Federation. It does NOT remove a pod from a federation. + + .PARAMETER Podname + The name of the pod to be removed. + + .PARAMETER Force + This can be used to forcefully remove a pod from the pod federation. This can only be done while connected to one of the other pods in the federation + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + Unregister-hvpod -podname PODNAME + Checks if you are connected to the pod and gracefully unregisters it from the podfedaration + + .EXAMPLE + Unregister-hvpod -podname PODNAME -force + Checks if you are connected to the pod and gracefully unregisters it from the podfedaration + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.3.2,7.4 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $true)] + [string] + $PodName, + + [Parameter(Mandatory = $false)] + [bool] + $force, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + $pods=$services.pod.pod_list() + $pod=$pods | where-object {$_.displayname -like "$podname"} + if ($force -eq $false){ + if ($pod.localpod -eq $False){ + Write-Error "You can only gracefully remove a pod when connected to that pod, please connect to a connection server in pod $podname" + break + } + elseif ($pod.localpod -eq $True){ + write-host "Gracefully removing $podname from the federation" + $services.PodFederation.PodFederation_Unjoin() + } + } + + elseif ($force -eq $true){ + if ($pod.localpod -eq $True){ + Write-Error "You can only forcefully remove a pod when connected to a different pod, please connect to a connection server in another pod then $podname" + break + } + elseif ($pod.localpod -eq $false){ + write-host "Forcefully removing $podname from the federation" + $services.PodFederation.PodFederation_eject($pod.id) + } + } + + + + [System.gc]::collect() +} + +function set-hvpodfederation { + <# + .Synopsis + Used to change the name of a Horizon View Pod Federation (Cloud Pod Architecture) + + .DESCRIPTION + Used to change the name of a Horizon View Pod Federation (Cloud Pod Architecture) + + .PARAMETER Name + The new name of the Pod Federation. + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + set-hvpodfederation -name "New Name" + Will update the name of the current podfederation. + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.3.2,7.4 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $true)] + [string] + $name, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + $podservice=new-object vmware.hv.podfederationservice + $podservicehelper=$podservice.read($services) + $podservicehelper.getDatahelper().setdisplayname($name) + $podservice.update($services, $podservicehelper) + get-hvpodfederation + + [System.gc]::collect() +} + +function get-hvsite { + <# + .Synopsis + Returns information about the sites within a Horizon View Pod Federation (Cloud Pod Architecture) + + .DESCRIPTION + Returns information about the sites within a Horizon View Pod Federation (Cloud Pod Architecture) + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + get-hvsite + Returns information about the sites within a Horizon View Pod Federation. + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.3.2,7.4 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + $hvsites=$services1.site.site_list() + return $hvsites + + [System.gc]::collect() +} + +function new-hvsite { + <# + .Synopsis + Creates a new site within a Horizon View Pod Federation (Cloud Pod Architecture) + + .DESCRIPTION + Creates a new site within a Horizon View Pod Federation (Cloud Pod Architecture) + + .PARAMETER Name + Name of the site (required) + + .PARAMETER Description + Description of the site (required) + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + new-hvsite -name "NAME" -description "DESCRIPTION" + Returns information about the sites within a Horizon View Pod Federation. + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.3.2,7.4 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $true)] + [string] + $name, + + [Parameter(Mandatory = $true)] + [string] + $description, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + $sitebase=new-object vmware.hv.sitebase + $sitebase.displayname=$name + $sitebase.description=$description + $services.site.site_create($sitebase) + + [System.gc]::collect() +} + +function set-hvsite { + <# + .Synopsis + renames a new site within a Horizon View Pod Federation (Cloud Pod Architecture) + + .DESCRIPTION + renames a new site within a Horizon View Pod Federation (Cloud Pod Architecture) + + .PARAMETER Sitename + Name of the site to be edited + + .PARAMETER Name + New name of the site (required) + + .PARAMETER Description + New description of the site (required) + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + set-hvsite -site "CURRENTSITENAME" -name "NAME" -description "DESCRIPTION" + Returns information about the sites within a Horizon View Pod Federation. + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.3.2,7.4 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $true)] + [string] + $sitename, + + [Parameter(Mandatory = $true)] + [string] + $name, + + [Parameter(Mandatory = $true)] + [string] + $description, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + $siteid=$services1.site.site_list() | where-object {$_.base.displayname -like $sitename} + $siteservice=new-object vmware.hv.siteservice + $sitebasehelper=$siteservice.read($services, $siteid.id) + $sitebasehelper.getbasehelper().setdisplayname($name) + $sitebasehelper.getbasehelper().setdescription($description) + $siteservice.update($services, $sitebasehelper) + + [System.gc]::collect() +} + +function remove-hvsite { + <# + .Synopsis + renames a new site within a Horizon View Pod Federation (Cloud Pod Architecture) + + .DESCRIPTION + renames a new site within a Horizon View Pod Federation (Cloud Pod Architecture) + + .PARAMETER Name + Name of the site (required) + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + set-hvsite -site "CURRENTSITENAME" -name "NAME" -description "DESCRIPTION" + Returns information about the sites within a Horizon View Pod Federation. + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.3.2,7.4 + PowerCLI Version : PowerCLI 6.5, PowerCLI 6.5.1 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $true)] + [string] + $name, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + $siteid=$services1.site.site_list() | where-object {$_.base.displayname -like $name} + $services.site.site_delete($siteid.id) + + [System.gc]::collect() +} + +function Get-HVHomeSite { + <# + .Synopsis + Gets the configured Horizon View Homesites + + .DESCRIPTION + Gets the configured Horizon View Homesites + + .PARAMETER Group + User principal name of a group + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + Get-HVHomeSite + + .EXAMPLE + Get-HVHomeSite -group group@domain + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.4 + PowerCLI Version : PowerCLI 10 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $false)] + [ValidatePattern("^.+?[@\\].+?$")] + [String] + $Group, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + begin{ + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + + process{ + $hsresults = Get-HVQueryResult -EntityType UserHomeSiteInfo -HvServer $HvServer + $resultsoverview=@() + + foreach ($hsresult in $hsresults){ + if ($hsresult.Globalentitlement){ + $Globalentitlement = ($services.GlobalEntitlement.GlobalEntitlement_Get($hsresult.base.GlobalEntitlement)).base.displayname + } + else{ + $Globalentitlement="" + } + + if (($hsresult.globalapplicationEntitlement)){ + $GlobalApplicationEntitlement = ($services.GlobalApplicationEntitlement.GlobalApplicationEntitlement_Get($hsresult.base.GlobalApplicationEntitlement)).base.displayname + } + else{ + $GlobalApplicationEntitlement="" + } + + $resultsoverview+=New-Object PSObject -Property @{"id" = ($hsresult).id; + "Group" = ($services.ADUserOrGroup.ADUserOrGroup_Get(($hsresult.base).userorgroup)).base.displayname; + "Site" = ($services.site.site_Get($hsresult.base.site)).base.displayname; + "Globalentitlement" =$Globalentitlement; + "Globalapplicationentitlement" =$GlobalApplicationEntitlement; + } + } + if ($group){ + $results=$resultsoverview | where-object {$_.Group -eq $group} | select-object id,Group,Site,Globalentitlement,GlobalApplicationEntitlement + } + else{ + $results=$resultsoverview | select-object id,Group,Site,Globalentitlement,GlobalApplicationEntitlement + } + return $results + [System.gc]::collect() + } + +} + +function New-HVHomeSite { + <# + .Synopsis + Defines a homesite within a Horizon View Cloud Pod architecture + + .DESCRIPTION + Creates a new homesite within a Cloud Pod Archtitecture. By default it will be applied to everything + but the choice can be made to only apply for a single global entitlement or singel global application entitlement + + .PARAMETER Group + User principal name of a group + + .PARAMETER Site + Name of the Horizon View Site + + .PARAMETER globalEntitlement + Name of the global entitlement + + .PARAMETER globalApplicationEntitlement + Name of the global application entitlement + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + New-HVHomeSite -group group@domain -site SITE + + .EXAMPLE + New-HVHomeSite -group group@domain -site SITE -globalapplicationentitlement ge-ap01 + + .EXAMPLE + New-HVHomeSite -group group@domain -site SITE -globalentitlement GE_Production + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.4 + PowerCLI Version : PowerCLI 10.1.1 + PowerShell Version : 5.0 + #> + + + [CmdletBinding(DefaultParameterSetName="Default")] + param( + [Parameter(Mandatory = $true,ParameterSetName="Default")] + [Parameter(ParameterSetName = "globalEntitlement")] + [Parameter(ParameterSetName = "globalApplicationEntitlement")] + [ValidatePattern("^.+?[@\\].+?$")] + [String] + $Group, + + [Parameter(Mandatory = $true,ParameterSetName="Default")] + [Parameter(ParameterSetName = "globalEntitlement")] + [Parameter(ParameterSetName = "globalApplicationEntitlement")] + [String] + $Site, + + [Parameter(Mandatory = $false,ParameterSetName="globalEntitlement")] + [String] + $globalEntitlement , + + [Parameter(Mandatory = $false,ParameterSetName="globalApplicationEntitlement")] + [String] + $globalApplicationEntitlement , + + [Parameter(Mandatory = $false,ParameterSetName="Default")] + [Parameter(ParameterSetName = "globalEntitlement")] + [Parameter(ParameterSetName = "globalApplicationEntitlement")] + $HvServer = $null + ) + + begin{ + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + } + + process { + $confirmFlag = Get-HVConfirmFlag -keys $PsBoundParameters.Keys + $groupinfo = Get-UserInfo -UserName $Group + $UserOrGroupName = $groupinfo.Name + $Domain = $groupinfo.Domain + $filter1 = Get-HVQueryFilter 'base.name' -Eq $UserOrGroupName + $filter2 = Get-HVQueryFilter 'base.domain' -Eq $Domain + $filter3 = Get-HVQueryFilter 'base.group' -Eq $true + $andFilter = Get-HVQueryFilter -And -Filters @($filter1, $filter2, $filter3) + $adresults = Get-HVQueryResult -EntityType ADUserOrGroupSummaryView -Filter $andFilter -HvServer $HvServer + + if ($adresults.length -ne 1) { + Write-host "Unable to find specific group with given search parameters" + break + } + $userhomesitebase=new-object VMware.Hv.UserHomeSiteBase + $userhomesitebase.UserOrGroup=$adresults.id + $userhomesitebase.site=($services.site.site_list() | where-object {$_.base.displayname -eq $site}).id + if ($globalEntitlement){ + $geresults = Get-HVQueryResult -EntityType GlobalEntitlementSummaryView -HvServer $HvServer + $geid=$geresults | where-object {$_.base.displayname -eq $globalEntitlement} + $userhomesitebase.globalEntitlement=$geid.id + } + elseif($globalApplicationEntitlement){ + $gaefilter1 = Get-HVQueryFilter 'data.name' -Eq $globalApplicationEntitlement + $gaeresults = Get-HVQueryResult -EntityType ApplicationInfo -Filter $gaefilter1 -HvServer $HvServer + $userhomesitebase.GlobalApplicationEntitlement=$gaeresults.id + } + $services.UserHomeSite.UserHomeSite_Create($userhomesitebase) + } +} + +function Set-HVEventDatabase { + <# + .Synopsis + Registers or changes a Horizon View Event database. + + .DESCRIPTION + Registers or changes a Horizon View Event database + + .PARAMETER ServerName + Name of the database server (Required) + + .PARAMETER Databasetype + Database type, possible options: MYSQL,SQLSERVER,ORACLE. Defaults to SQLSERVER + + .PARAMETER DatabasePort + Port number on the database server to which View will send events. Defaults to 1433. + + .PARAMETER Databasename + Name of the Database (required) + + .PARAMETER TablePrefix + Prefix to use for the Event Databse. Allowed characters are letters, numbers, and the characters @, $, #, _, and may not be longer than 6 characters. + + .PARAMETER UserName + UserName to connect to the database (required) + + .PARAMETER Password + Password of the user connecting to the database in Securestring format. + Can be created with: $password = Read-Host 'Domain Password' -AsSecureString + + .PARAMETER eventtime + Time to show the events for. Possible options are ONE_WEEK, TWO_WEEKS, THREE_WEEKS, ONE_MONTH,TWO_MONTHS, THREE_MONTHS, SIX_MONTHS + + .PARAMETER EventNewTime + Time in days to classify events for new. Range 1-3 + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + register-hveventdatabase -server SERVER@domain -database DATABASENAME -username USER@domain -password $password + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.4 + PowerCLI Version : PowerCLI 10 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $true)] + [string] + $ServerName, + + [Parameter(Mandatory = $false)] + [string] + $DatabaseType = "SQLSERVER", + + [Parameter(Mandatory = $false)] + [int] + $DatabasePort = 1433, + + [Parameter(Mandatory = $true)] + [string] + $DatabaseName, + + [Parameter(Mandatory = $false)] + [string][ValidateLength(1,6)] + $TablePrefix, + + [Parameter(Mandatory = $true)] + + [String] + $UserName, + + [Parameter(Mandatory = $true)] + [securestring] + $password, + + [Parameter(Mandatory = $false)] + [ValidateSet('ONE_WEEK','TWO_WEEKS','THREE_WEEKS','ONE_MONTH','TWO_MONTHS','THREE_MONTHS','SIX_MONTHS')] + [string] + $eventtime="TWO_WEEKS", + + [Parameter(Mandatory = $false)] + [ValidateRange(1,3)] + [int] + $eventnewtime = 2, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + $temppw = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password) + $PlainevdbPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($temppw) + $dbupassword = New-Object VMware.Hv.SecureString + $enc = [system.Text.Encoding]::UTF8 + $dbupassword.Utf8String = $enc.GetBytes($PlainevdbPassword) + + $eventservice=new-object vmware.hv.eventdatabaseservice + $eventservicehelper=$eventservice.getEventDatabaseInfoHelper() + $eventsettings=new-object VMware.Hv.EventDatabaseEventSettings + $eventdatabase=new-object VMware.Hv.EventDatabaseSettings + $eventsettings.ShowEventsForTime=$eventtime + $eventsettings.ClassifyEventsAsNewForDays=$eventnewtime + $eventdatabase.Server=$ServerName + $eventdatabase.type=$DatabaseType + $eventdatabase.port=$DatabasePort + $eventdatabase.name=$DatabaseName + $eventdatabase.username=$UserName + if($TablePrefix){ + $eventdatabase.tablePrefix=$tableprefix + } + + $eventdatabase.password=$dbupassword + $eventservicehelper.setDatabase($eventdatabase) + $eventservicehelper.setsettings($eventsettings) + $eventservice.update($services, $eventservicehelper) + + [System.gc]::collect() +} + +function Get-HVEventDatabase { + <# + .Synopsis + Retreives information about the configured Event Database + + .DESCRIPTION + Collects information about the configured event database for aHorizon View pod + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + Get-HVEventDatabase + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.4 + PowerCLI Version : PowerCLI 10 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + + $eventdatabase=$services.EventDatabase.EventDatabase_Get() + if ($eventdatabase.eventdatabaseset -eq $False){ + write-output "No Event Database configuration found" + } + elseif ($eventdatabase.eventdatabaseset -eq $true){ + $Eventdatabaseoverview=@() + $Eventdatabaseoverview+=New-Object PSObject -Property @{"Servername" = $eventdatabase.Database.Server; + "Type" = $eventdatabase.Database.Type; + "DatabaseName" = $eventdatabase.Database.Name; + "UserName" = $eventdatabase.Database.UserName; + "TablePrefix" = $eventdatabase.Database.TablePrefix; + "ShowEventsForTime" = $eventdatabase.settings.ShowEventsForTime; + "ClassifyEventsAsNewForDays" = $eventdatabase.settings.ClassifyEventsAsNewForDays; + } | select-object Servername,Type,DatabaseName,UserName,TablePrefix,ShowEventsForTime,ClassifyEventsAsNewForDays + } + return $Eventdatabaseoverview + [System.gc]::collect() +} + +function Clear-HVEventDatabase { + <# + .Synopsis + Clears configurationof the configured Event Database + + .DESCRIPTION + Clears configurationof the configured Event Database + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + Clear-HVEventDatabase + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.4 + PowerCLI Version : PowerCLI 10 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $true, + ConfirmImpact = 'High')] + + param( + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + PROCESS { + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + if ($pscmdlet.ShouldProcess($($msg))) { + $services.EventDatabase.EventDatabase_Clear() + } + [System.gc]::collect() + } +} + +function Set-HVlicense { + <# + .Synopsis + Sets or changes the license for Horizon View + + .DESCRIPTION + Sets or changes the license for Horizon View + + .PARAMETER license + License key (string) + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + set-hvlicense -license "LICENSE-KEY" + Returns information about the sites within a Horizon View Pod Federation. + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.4,7.5 + PowerCLI Version : PowerCLI 10 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $true)] + [string] + $license, + + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + + try { + $services.license.license_set($license) + } + catch { + write-error $_.exception message + break + } + $licenseresult=$services.license.license_get() + return $licenseresult + [System.gc]::collect() +} + + +function Get-HVlicense { + <# + .Synopsis + Gets the license for Horizon View + + .DESCRIPTION + Gets the license for Horizon View + + + .PARAMETER HvServer + Reference to Horizon View Server to query the virtual machines from. If the value is not passed or null then + first element from global:DefaultHVServers would be considered in-place of hvServer + + .EXAMPLE + get-hvlicense + + + .NOTES + Author : Wouter Kursten + Author email : wouter@retouw.nl + Version : 1.0 + + ===Tested Against Environment==== + Horizon View Server Version : 7.4,7.5 + PowerCLI Version : PowerCLI 10 + PowerShell Version : 5.0 + #> + + [CmdletBinding( + SupportsShouldProcess = $false, + ConfirmImpact = 'High' + )] + + param( + [Parameter(Mandatory = $false)] + $HvServer = $null + ) + + + $services = Get-ViewAPIService -hvServer $hvServer + if ($null -eq $services) { + Write-Error "Could not retrieve ViewApi services from connection object" + break + } + + $license=$services.license.license_get() + return $license + [System.gc]::collect() +} + +Export-ModuleMember Add-HVDesktop,Add-HVRDSServer,Connect-HVEvent,Disconnect-HVEvent,Get-HVPoolSpec,Get-HVInternalName, Get-HVEvent,Get-HVFarm,Get-HVFarmSummary,Get-HVPool,Get-HVPoolSummary,Get-HVMachine,Get-HVMachineSummary,Get-HVQueryResult,Get-HVQueryFilter,New-HVFarm,New-HVPool,Remove-HVFarm,Remove-HVPool,Set-HVFarm,Set-HVPool,Start-HVFarm,Start-HVPool,New-HVEntitlement,Get-HVEntitlement,Remove-HVEntitlement, Set-HVMachine, New-HVGlobalEntitlement, Remove-HVGlobalEntitlement, Get-HVGlobalEntitlement, Set-HVApplicationIcon, Remove-HVApplicationIcon, Get-HVGlobalSettings, Set-HVGlobalSettings, Set-HVGlobalEntitlement, Get-HVResourceStructure, Get-HVLocalSession, Get-HVGlobalSession, Reset-HVMachine, Remove-HVMachine, Get-HVHealth, New-HVPodfederation, Remove-HVPodFederation, Get-HVPodFederation, Register-HVPod, Unregister-HVPod, Set-HVPodFederation,Get-HVSite,New-HVSite,Set-HVSite,Remove-HVSite,New-HVHomeSite,Get-HVHomeSite,Set-HVEventDatabase,Get-HVEventDatabase,Clear-HVEventDatabase,Get-HVlicense,Set-HVlicense diff --git a/Modules/VMware.VCGChecker/Export-VCGReport.ps1 b/Modules/VMware.VCGChecker/Export-VCGReport.ps1 new file mode 100644 index 0000000..3774915 --- /dev/null +++ b/Modules/VMware.VCGChecker/Export-VCGReport.ps1 @@ -0,0 +1,1057 @@ +# +# Generate the html report and save it to the report folder +# TODO: change to the class +Function Export-VCGReport { + Param( + [Parameter(Mandatory=$true)] $Data, + [Parameter(Mandatory=$true)] $Dir + ) + if (!(Test-Path $Dir)) { + New-Item -Type directory -Confirm:$false -Path $Dir -Force |Out-Null + } + + $Data,$flag = refactorData $Data + + $null=Generate_CsvReport $Data $Dir + $null=Generate_HtmlReport $Data $Dir + $null=Generate_SummaryReport $Data $Dir +} +Function Generate_HtmlReport($Data, $Dir) { + info ("Generating compatibility detail report...") + $content = $generalHead + $content += '

ESXi Hardware Compatibility Report for {0}

' -f $Data.hostname + $content += $generalBodyBase + if (-not(checkVersion($Data))) { + $content += 'Installed Release' + } + else { + $content += 'Checked Release' + } + $content += $generalBodyRest + foreach ($VCResource in $Data) { + $checkVersion = $VCResource.checkRelease + $Count1 = $VCResource.DCResource.HostResource.ComponentResource.Count + $Process = '' + if ($VCResource.hostname -ne 'null') { + $Process += '{1}' -f $Count1, [string]$VCResource.hostname + } + else { + $Process += '{1}' -f $Count1, '/' + } + foreach ($DCResource in $VCResource.DCResource) { + $Count2 = $DCResource.HostResource.ComponentResource.Count + if ($DCResource.dcname -ne 'null') { + $Process += '{1}' -f [string]$Count2, [string]$DCResource.dcname + } + else { + $Process += '{1}' -f [string]$Count2, '/' + } + foreach ($HostResourceOne in $DCResource.HostResource) { + $Count = $HostResourceOne.ComponentResource.count + $Process += '{1}' -f $Count, $HostResourceOne.HostName + foreach ($OneDevice in $HostResourceOne.ComponentResource) { + if ($OneDevice.type -eq 'Server') { + # $InfoServer = "" | Select HostName, Manufacturer, Model, CPU, Version, Bios, CpuFeature, Uuid, VcgLink, Status, FoundRelease, MatchResult, Warnings + $InfoServer = @{} + $InfoServer.HostName = $HostResourceOne.hostname + $InfoServer.Version = $HostResourceOne.version + $InfoServer.Manufacturer = $OneDevice.vendor + $InfoServer.Model = $OneDevice.model + $InfoServer.CPU = $OneDevice.cpumodel + $InfoServer.Bios = $OneDevice.biosversion + $InfoServer.CpuFeature = $OneDevice.cpufeatureid + $InfoServer.Uuid = $OneDevice.uuid + $InfoServer.VcgLink = $OneDevice.vcgLink + $InfoServer.Status = formatStatus($OneDevice.status) + $InfoServer.MatchResult = $OneDevice.matchResult + $InfoServer.Warnings = (([string]$OneDevice.warnings) -split "More information")[0] + # server info + $Process += 'Server' + $Process += '{0}' -f $InfoServer.Model + $Process += '{0}' -f $InfoServer.Manufacturer + if (-not $checkVersion) { + $Process += '{0}' -f $InfoServer.Version + } + else { + $Process += '{0}' -f $checkVersion + } + $Process += '{0}' -f $InfoServer.Status + $Process += '{0},
CpuFeature:{1},
Bios:{2}' -f $InfoServer.CPU, $InfoServer.CpuFeature, $InfoServer.Bios + if ($InfoServer.VcgLink.Count -gt 0) { + $Process += '{0}
' -f $InfoServer.Warnings + foreach ($link in $InfoServer.VcgLink) { + $Process += ' VCG link
' -f $link + } + $Process += '' + } + else + {$Process += '{0}
N/A' -f $InfoServer.Warnings} + $Process += '' + } + else { + # $InfoPci = "" | Select model, vendor, Vid, Did, Svid, Ssid, Driver, DriverVersion, Version, Pccid, VcgLink, Status, FoundRelease, MatchResult, Warnings + $InfoPci = @{} + $InfoPci.model = $OneDevice.model + $InfoPci.vendor = $OneDevice.vendor + $InfoPci.Vid = $OneDevice.vid + $InfoPci.Did = $OneDevice.did + $InfoPci.Svid = $OneDevice.svid + $InfoPci.Ssid = $OneDevice.ssid + $InfoPci.Driver = $OneDevice.driver + $InfoPci.DriverVersion = $OneDevice.driverversion + $InfoPci.Version = $HostResourceOne.version + $InfoPci.Pccid = $OneDevice.pciid + $InfoPci.VcgLink = $OneDevice.vcgLink + $InfoPci.Status = formatStatus($OneDevice.status) + $InfoPci.MatchResult = $OneDevice.matchResult + $InfoPci.Warnings = (([string]$OneDevice.warnings) -split "More information")[0] + # IO info + # TODO:Variable information coverage, need to be modified + $Process += '' + $Process += 'IO Device' + $Process += '{0}' -f $InfoPci.model + $Process += '{0}' -f $InfoPci.vendor + if (-not $checkVersion) { + $Process += '{0}' -f $InfoPci.Version + } + else { + $Process += '{0}' -f $checkVersion + } + $Process += '{0}' -f $InfoPci.Status + $Process += 'PCI ID:{0}
Driver:{1} Version:{2}' -f $InfoPci.Pccid, $InfoPci.Driver, $InfoPci.DriverVersion + if ($InfoPci.VcgLink.Count -gt 0) { + $Process += '{0}
' -f $InfoPci.Warnings + foreach ($link in $InfoPci.VcgLink) { + $Process += ' VCG link
' -f $link + } + $Process += '' + } + else + {$Process += '{0}
N/A' -f $InfoPci.Warnings} + $Process += '' + } + } + } + } + $content += $Process + } + $content += $generalFooter + #define filename and filepath + $dataTime = Get-Date -Format 'yyyy-M-d_h-m' + $vcName = vcName($Data) + $filename = 'compreport_' + $vcName + $dataTime + '.html' + $filePath = $Dir + '\' + $filename + #save report + $content |Out-File -FilePath $filePath -Encoding utf8| Out-Null + info ("Report " + "'" + $filePath + "'" + " has been created!") +} + +Function Generate_SummaryReport($Data, $Dir) { + info ("Generating compatibility summary report...") + $content = $summaryHead + $vcCount = $Data.length + $barsWidth = 45 / ($vcCount+1) + $content += 'var barsWidth = {0};' -f $barsWidth + # get vCName arry and host count + $vcNameArray = @() + foreach ($DCResource in $Data) { + $vcNameArray += $DCResource.DCResource[0].vcname + } + + $content += "var vCname = [" + foreach ($vc in $vcNameArray) { + $content += '"{0}",' -f $vc + } + $content += "];" + # Count Compatible or unpgrade + $compatibleCountFormat = '[' + $mayNotCompatibleCountFormat = '[' + $UnabletoUpgradeCountFormat = '[' + foreach ($VCResource in $Data) { + $checkVersion = $VCResource.checkRelease + $compatibleCount = 0 + $notcompatibleCount = 0 + $UnabletoUpgradeCount = 0 + foreach ($DCResource in $VCResource.DCResource) { + foreach ($HostResourceOne in $DCResource.HostResource) { + foreach ($OneDevice in $HostResourceOne.ComponentResource) { + $flagcompatible = 0 + # if($checkVersion){ + # if(-not($OneDevice.upgrade)){ + # $flagcompatible = 'null' + # break + # } + # } + #whether compatible + if ($OneDevice.status -ne 'Compatible') { + $flagcompatible = 0 + break + } + else { + $flagcompatible = 1 + } + #whether upgrade + } + + if ($flagcompatible -eq 1) { + $compatibleCount += 1 + } + elseif($flagcompatible -eq 0){ + $notcompatibleCount += 1 + } + + elseif($flagcompatible -eq 'null') { + $UnabletoUpgradeCount += 1 + } + } + } + $compatibleCountFormat += $compatibleCount + $compatibleCountFormat += ',' + $mayNotCompatibleCountFormat += $notcompatibleCount + $mayNotCompatibleCountFormat += ',' + $UnabletoUpgradeCountFormat += $UnabletoUpgradeCount + $UnabletoUpgradeCountFormat += ',' + } + $compatibleCountFormat += ',]' + $mayNotCompatibleCountFormat += ',]' + $UnabletoUpgradeCountFormat += ']' + + $dataDict = [Ordered]@{} + + if (-not(checkVersion($Data))) { + $content += 'var lengendSeries = ["Compatible","May Not Compatible"];' + $content += $hostCountHead + + $dataDict.Insert(0, "Compatible", $compatibleCountFormat) + $dataDict.Insert(1, "May Not Compatible", $mayNotCompatibleCountFormat) + } + else { + $content += 'var lengendSeries = ["Compatible","May Not Compatible","Unable to upgrade"];' + $content += $hostCountHead + + $dataDict.Insert(0, "Compatible", $compatibleCountFormat) + $dataDict.Insert(1, "May Not Compatible", $mayNotCompatibleCountFormat) + $dataDict.Insert(2, "Unable to upgrade", $UnabletoUpgradeCountFormat) + } + [System.Collections.IEnumerator]$dataDict = $dataDict.Keys.GetEnumerator(); + $formatContent = formatHostCountGraphic $dataDict + + $content += $formatContent + $content += $hostCountRest + + + # Host Compatibility + $compatibleCount = 0 + $mayNotcompatibleCount = 0 + $unableUpgradeCount = 0 + foreach ($VCResource in $Data) { + foreach ($DCResource in $VCResource.DCResource) { + foreach ($HostResourceOne in $DCResource.HostResource) { + foreach ($OneDevice in $HostResourceOne.ComponentResource) { + #whether compatible + $flagcompatible = 0 + # if ($checkVersion){ + # if (-not($OneDevice.upgrade)){ + # $flagcompatible = 'null' + # break + # } + # } + if ($OneDevice.status -ne 'Compatible') { + $flagcompatible = 0 + break + } + else { + $flagcompatible = 1 + } + + } + if ($flagcompatible -eq 1) { + $compatibleCount += 1 + } + elseif ($flagcompatible -eq 0) { + $mayNotcompatibleCount += 1 + } + elseif ($flagcompatible -eq 'null') { + $unableUpgradeCount += 1 + } + } + } + } + + if (-not(checkVersion($Data))) { + $content += "var seriesCount = {'Compatible':$compatibleCount, 'May Not Compatible':$mayNotcompatibleCount,};" + $content += "var lengendSeries = ['Compatible','May Not Compatible'];" + $content += "var catalog = [ + {value:seriesCount['Compatible'], name:'Compatible'}, + {value:seriesCount['May Not Compatible'], name:'May Not Compatible'}, + ];" + $content += "var colors = [colorsC,colorsM];" + } + else { + $content += "var seriesCount = {'Compatible':$compatibleCount, 'May Not Compatible':$mayNotcompatibleCount,'Unable to Upgrade':$unableUpgradeCount};" + $content += "var lengendSeries = ['Compatible','May Not Compatible','Unable to Upgrade'];" + $content += "var catalog = [ + {value:seriesCount['Compatible'], name:'Compatible'}, + {value:seriesCount['May Not Compatible'], name:'May Not Compatible'}, + {value:seriesCount['Unable to Upgrade'], name:'Unable to Upgrade'}, + ];" + $content += "var colors = [colorsC,colorsM,colorsU];" + } + + $content += $hostCompatible + + + #Host Model Compatibility by vCenter + if (-not(checkVersion($Data))) { + $serverSource = "[['series', 'Compatible', 'May Not Compatible',]," + } + else { + $serverSource = "[['series', 'Compatible','May Not compatible','No Upgrade Path']," + } + + foreach ($VCResource in $Data) { + $compatibleCount = 0 + $mayNotCompatibleCount = 0 + $unableUpgradeCount = 0 + $checkVersion = $VCResource.checkRelease + foreach ($DCResourceOne in $VCResource.DCResource) { + foreach ($HostResourceOne in $DCResourceOne.HostResource) { + $flagcompatible = 0 + foreach ($ComoenentResourceOne in $HostResourceOne.ComponentResource) { + #whether compatible + if ($ComoenentResourceOne.type -eq 'Server') { + # if ($checkVersion) { + # if (-not($ComoenentResourceOne.upgrade)) { + # $flagcompatible = 'null' + # break + # } + # } + if ($ComoenentResourceOne.status -ne 'Compatible') { + $flagcompatible = 0 + } + else { + $flagcompatible = 1 + } + break + + } + } + if ($flagcompatible -eq 1) { + $compatibleCount += 1 + } + elseif ($flagcompatible -eq 0) { + $mayNotcompatibleCount += 1 + } + elseif ($flagcompatible -eq 'null') { + $unableUpgradeCount += 1 + } + } + } + if (-not $checkVersion) { + $serverSource += '["{0}",{1},{2}],' -f $DCResourceOne.vcname, $compatibleCount, $mayNotCompatibleCount + } + else { + $serverSource += '["{0}",{1},{2},{3}],' -f $DCResourceOne.vcname, $compatibleCount, $mayNotCompatibleCount, $unableUpgradeCount + } + } + $serverSource += '];' + $content += 'var serverSource = {0}' -f $serverSource + $content += $hostModelCompatibleHead + + if (-not $checkVersion) { + $colorsArray = 'colorsC', 'colorsM' + } + else { + $colorsArray = 'colorsC', 'colorsM', 'colorsU' + } + foreach ($colors in $colorsArray) { + $content += $hostModelCompatibleBody + $content += "color:$colors}," + } + $content += $hostModelCompatibleRest + + #count IO Device compatibility by vCenter + if (-not(checkVersion($Data))) { + $ioSource = "[['series', 'Compatible', 'May Not Compatible',]," + } + else { + $ioSource = "[['series', 'Compatible','May Not Compatible','No Upgrade Path',]," + } + + foreach ($VCResource in $Data) { + $compatibleCount = 0 + $mayNotCompatibleCount = 0 + $unableUpgradeCount = 0 + $checkVersion = $VCResource.checkRelease + foreach ($DCResourceOne in $VCResource.DCResource) { + foreach ($HostResourceOne in $DCResourceOne.HostResource) { + $flagcompatible = 0 + foreach ($ComoenentResourceOne in $HostResourceOne.ComponentResource) { + if ($ComoenentResourceOne.type -eq 'IO Device') { + # if ($checkVersion) { + # if (-not($ComoenentResourceOne.upgrade)) { + # $flagcompatible = 'null' + # } + # } + if ($ComoenentResourceOne.status -ne 'Compatible') { + $flagcompatible = 0 + } + else { + $flagcompatible = 1 + } + + if ($flagcompatible -eq 1) { + $compatibleCount += 1 + } + elseif ($flagcompatible -eq 0) { + $mayNotCompatibleCount += 1 + } + elseif ($flagcompatible -eq 'null') { + $unableUpgradeCount += 1 + } + } + } + + } + } + if (-not $checkVersion) { + $ioSource += '["{0}",{1},{2}],' -f $DCResourceOne.vcname, $compatibleCount, $mayNotCompatibleCount + } + else { + $ioSource += '["{0}",{1},{2},{3}],' -f $DCResourceOne.vcname, $compatibleCount, $mayNotCompatibleCount, $unableUpgradeCount + } + } + + $ioSource += '];' + $content += 'var ioSource = {0}' -f $ioSource + $content += $ioCompatibleHead + if (-not $checkVersion) { + $colorsArray = 'colorsC', 'colorsM' + } + else { + $colorsArray = 'colorsC', 'colorsM', 'colorsU' + } + foreach ($colors in $colorsArray) { + $content += $ioCompatibleBody + $content += "color:$colors}," + } + $content += $ioCompatibleRest + + $dataTime = Get-Date -Format 'yyyy-M-d_h-m' + $vcName = vcName($Data) + $filename = 'sumreport_' + $vcName + $dataTime + '.html' + $filePath = $Dir + '\' + $filename + # Out-put a html report + $content |Out-File -FilePath $filePath -Encoding utf8| Out-Null + info ("Report " + "'" + $filePath + "'" + " has been created!") +} + +Function Generate_CsvReport($Data, $Dir) { + info("Generating compatibility csv report") + #define header + $content = '' + $content += "VC," + $content += "DataCenter," + $content += "Host," + $content += "Type," + $content += "Model Name," + $content += "Vendor," + if (-not(checkVersion($Data))) { + $content += "Installed Release," + } + else { + $content += "Checked Release," + } + $content += "Compatible Status," + $content += "Hardware Detail," + $content += "Comments," + $content += 'VCG Link,' + $content += "`n" + + #formate content + foreach ($VCResource in $Data) { + $checkVersion = $VCResource.checkRelease + foreach ($DCResourceOne in $VCResource.DCResource) { + foreach ($HostResourceOne in $DCResourceOne.HostResource) { + $installVersion = $HostResourceOne.version + foreach ($ComoenentResourceOne in $HostResourceOne.ComponentResource) { + if ($DCResourceOne.vcname -ne 'null') { + $content += "{0}," -f $DCResourceOne.vcname + } + else { + $content += "{0}," -f '/' + } + #DataCenterDetail + if ($DCResourceOne.dcname -ne 'null') { + $content += "{0}," -f $DCResourceOne.dcname + } + else { + $content += "{0}," -f '/' + } + #Host + $content += "{0}," -f $HostResourceOne.hostname + #Type + $content += "{0}," -f $ComoenentResourceOne.type + #Model Name + $content += '"{0}",' -f $ComoenentResourceOne.model + #Vendor + $content += '"{0}",' -f $ComoenentResourceOne.vendor + if (-not $checkVersion) { + #Installed Release + $content += '"{0}",' -f $installVersion + } + else { + $content += '"{0}",' -f $checkVersion + } + #Status + $content += '"{0}",' -f (formatStatus($ComoenentResourceOne.status)) + #CompatibleHardware + if ($ComoenentResourceOne.type -eq 'IO Device') { + $CompatibleHardware = "'PCI ID:{0}, Driver:{1} {2}" -f $ComoenentResourceOne.pciid, $ComoenentResourceOne.Driver, $ComoenentResourceOne.DriverVersion + } + else { + $CompatibleHardware = "'CPU: {0}(Feature:{1}) BIOS:{2}" -f $ComoenentResourceOne.cpumodel, $ComoenentResourceOne.cpufeatureid, $ComoenentResourceOne.biosversion + } + $content += '"{0}",' -f $CompatibleHardware + + #Comments + + if ($ComoenentResourceOne.Warnings.Count -gt 0) { + $Comments = $ComoenentResourceOne.Warnings + } + else { + $Comments ='N/A' + } + + $content += '"{0}",' -f $Comments + + #VCG Link + $VCGLink = '' + foreach ($link in $ComoenentResourceOne.VcgLink) { + $VCGLink += $link + $VCGLink += ' ' + } + $content += '"{0}",' -f $VCGLink + $content += "`n" + } + } + } + } + + #define filename and path + $dataTime = Get-Date -Format 'yyyy-M-d_h-m' + $vcName = vcName($Data) + $filename = 'compreport_' + $vcName + $dataTime + '.csv' + $filePath = $Dir + '\' + $filename + #save csv report + info ("Report " + "'" + $filePath + "'" + " has been created!") + $content |Out-File -FilePath $filePath -Encoding utf8| Out-Null + return $content +} +Function refactorData ($data) { + $DCResource,$flag = refactorDC $data + $data,$flag = refactorVC $DCResource + return $data, $true +} + +Function refactorDC($data) { + $DCResource = @() + $HostResource = @() + $HR = @{} + $DC = @{} + + $ReData = $data + + $HR.__type__ = $ReData[0].__type__ + $HR.vcname = $ReData[0].vcname + $HR.dcname = $ReData[0].dcname + $HR.hostname = $ReData[0].hostname + $HR.apitype = $ReData[0].apitype + $HR.powerstatus = $ReData[0].powerstatus + $HR.version = $ReData[0].version + $HR.fullname = $ReData[0].fullname + $HR.connectionstatus = $ReData[0].connectionstatus + $HR.ComponentResource = $ReData[0].ComponentResource + $HostResource += $HR # HostResource = [{'hostname':'10.110.126.170'}] + $DC.dcname = $ReData[0].dcname #$DC={'dcname':'ha-datacenter'} + $DC.vcname = $ReData[0].vcname + $DC.HostResource = $HostResource #$DC={'dcname':'ha-datacenter','HostResource':[{'hostname':'10.110.126.170'}]} + $DC.checkRelease = $ReData[0].checkRelease + + $DCResource += $DC #$DCResource = [{'dcname':'ha-datacenter','HostResource':[{'hostname':'10.110.126.170'},]}] + $dcname = $ReData[0].dcname + $vcname = $ReData[0].vcname + $DcIndex = 0 + for ($i = 1; $i -lt $ReData.Count; $i++) { + $temp = $ReData[$i] + $HR = @{} + if ($temp.dcname -eq $dcname -and $temp.vcname -eq $vcname) { + $HR.__type__ = $temp.__type__ + $HR.vcname = $temp.vcname + $HR.dcname = $temp.dcname + $HR.hostname = $temp.hostname + $HR.apitype = $temp.apitype + $HR.powerstatus = $temp.powerstatus + $HR.version = $temp.version + $HR.fullname = $temp.fullname + $HR.connectionstatus = $temp.connectionstatus + $HR.ComponentResource = $temp.ComponentResource + $DCResource[$DcIndex].HostResource += $HR + } + else { + $DcIndex += 1 + $DCResource += @{} #$DCResource = [{'dcname':'ha-datacenter','HostResource':[{'hostname':'10.110.126.170'},{'hostname':'10.110.126.171'}]},{}] + $DCResource[$DcIndex].dcname = $temp.dcname # [{'dcname':'ha-datacenter','HostResource':[{'hostname':'10.110.126.170'},{'hostname':'10.110.126.171'}]},{'dcname':'hw-datacenter'}] + $DCResource[$DcIndex].vcname = $temp.vcname + $DCResource[$DcIndex].checkRelease = $temp.checkRelease + $DCResource[$DcIndex].HostResource = @() # [{'dcname':'ha-datacenter','HostResource':[{'hostname':'10.110.126.170'},{'hostname':'10.110.126.171'}]},{'dcname':'hw-datacenter','HostResource':[]}] + $HR = @{} + $HR.__type__ = $temp.__type__ + $HR.vcname = $temp.vcname + $HR.dcname = $temp.dcname + $HR.hostname = $temp.hostname + $HR.apitype = $temp.apitype + $HR.powerstatus = $temp.powerstatus + $HR.version = $temp.version + $HR.fullname = $temp.fullname + $HR.connectionstatus = $temp.connectionstatus + $HR.ComponentResource = $temp.ComponentResource + $DCResource[$DcIndex].HostResource += $HR # [{'dcname':'ha-datacenter','HostResource':[{'hostname':'10.110.126.170'},{'hostname':'10.110.126.171'}]},{'dcname':'hw-datacenter','HostResource':[{'hostname':'10.110.126.173'}]}] + $dcname = $temp.dcname + $vcname = $temp.vcname + } + } + return $DCResource,$true + # return $DCResource +} + +Function refactorVC ($data) { + $VCResource = @() + $DCResource = @() + $VC = @{} + $DCResource += $data[0] + $VC.DCResource = $DCResource + $VC.hostname = $data[0].vcName + $VC.checkRelease = $data[0].checkRelease + + $VCResource += $VC + $vcname = $data[0].vcname + $VcIndex = 0 + for ($i = 1 ; $i -lt $data.Count; $i++){ + $temp = $data[$i] + if ($vcname -eq $temp.vcname) { + $VCResource[$VcIndex].DCResource += $temp + } + else { + $VcIndex += 1 + $DCResource = @() + $VC = @{} + $DCResource += $temp + $VC.hostname = $temp.vcName + $VC.DCResource = $DCResource + $VC.checkRelease = $temp.checkRelease + $VCResource += $VC + $vcname = $temp.vcname + } + } + return $VCResource,$true +} + +Function vcName($Data) { + $vcName = '' + foreach ($VCResource in $Data) { + $vcName += $VCResource.hostname + $vcName += '_' + } + return $vcName +} + +Function checkVersion($Data) { + foreach ($VCResource in $Data) { + $checkVersion = $VCResource.checkRelease + return $checkVersion + } +} + +Function formatStatus($status) { + if ($status -eq 'MayNotBeCompatible') { + return 'May Not Be Compatible' + } + else { + return $status + } +} + +Function formatHostCountGraphic($dataDict) { + while ($dataDict.MoveNext()) { + $items = $dataDict.Key + + if ($items -eq 'Compatible') { + $colors = 'colorsC' + } + elseif ($items -eq 'May Not Compatible') { + $colors = 'colorsM' + } + elseif ($items -eq 'Unable to upgrade') { + $colors = 'colorsU' + } + + $content += $hostCountBody + $items = "'{0}'" -f $items + $content += 'name: {0},' -f $items + $content += 'data: {0},' -f $dataDict.Value + $content += 'color: {0},' -f $colors + $content += '},' + } + + return $content +} + + +#Detail report +$generalHead = @' + + + + + + + + + ESXi Compatible Report + + + +'@ + +$generalBodyBase = @' +

[WARNING] The compatible status may not be fully accurate, please validate it with the official VMware Compatibility Guide

+ + +
+ + + + + + + + + + +'@ + +$generalBodyRest = @' + + + + + + +'@ + +$generalFooter = @' + +
VCDataCenterHostTypeModel NameVendorCompatible StatusHardware DetailComments
+
+ + + +'@ + + +#Summary report +$summaryHead = @' + + + + + Summary Report + + + + + + + +
+
+
+
+
+ +
+ + +'@ + diff --git a/Modules/VMware.VCGChecker/Get-VCGHWInfo.ps1 b/Modules/VMware.VCGChecker/Get-VCGHWInfo.ps1 new file mode 100644 index 0000000..229608f --- /dev/null +++ b/Modules/VMware.VCGChecker/Get-VCGHWInfo.ps1 @@ -0,0 +1,310 @@ +<# +Copyright 2018 VMware, Inc. All rights reserved. +#> + +# Class to manage Host resources +Class HostResource { + [VMware.VimAutomation.Types.VMHost] $vmhost + [string] $vcname + [string] $clustername + [string] $dcname + [string] $hostname + [string] $apitype + [string] $powerstatus + [string] $productname + [string] $version + [string] $fullname + [string] $connectionstatus + [string] $checkRelease + [int] $port + [Array] $ComponentResource = @() + [Array] $JsonProperties = @('__type__', 'dcname', 'vcname','clustername','hostname', 'apitype', + 'powerstatus', 'productname', 'version', 'fullname', 'connectionstatus','checkRelease') + + HostResource( + [VMware.VimAutomation.Types.VMHost] $vmhost) { + $this.vmhost = $vmhost + $view =$vmhost|Get-View + $vCenter_IP = $view.Summary.ManagementServerIp + if($vCenter_IP){ + $this.vcname =$vCenter_IP + $this.dcname = (Get-Datacenter -VMHost $vmhost).Name + $this.clustername = (Get-Cluster -VMHost $vmhost).Name + }else{ + $this.vcname =$this.vmhost.Name + } + $this.hostname = $this.vmhost.Name + $summary = $this.vmhost.ExtensionData.Summary + $this.powerstatus = $summary.runtime.powerState + $this.connectionstatus = $summary.runtime.connectionState + $this.apitype = $summary.Config.Product.apiType + $this.fullname = $summary.Config.Product.FullName + $this.version = $summary.Config.Product.version + $this.productname = $summary.Config.Product.licenseProductName + $this.port = 443 + } + + [Array] query_components() { + if ($this.ComponentResource.Count -eq 0) { + # Get server info + for($count_retry=0;$count_retry -lt 3;$count_retry ++){ + try{ + $svrResoure = [ServerResource]::new() + $svrResoure.set_data($this.vmhost) + $this.ComponentResource += $svrResoure + break + }catch{ + error('query components server for '+$this.vmhost.Name +' error, retry it ' +($count_retry+1) +' times') + } + } + # Get PCI devices + for($count_retry=0;$count_retry -lt 3;$count_retry ++){ + try{ + $this.query_pcidevices() + break + }catch{ + error('query components pcidevice for '+$this.vmhost.Name +' error, retry it ' +($count_retry+1) +' times') + if($count_retry -eq 2){ + error('query components pcidevice for '+$this.vmhost.Name +' faild') + } + } + } + } + return $this.ComponentResource + } + + [void] query_pcidevices() { + $EsxCliV2 = Get-EsxCli -V2 -VMHost $this.vmhost + $AllPciDevice = $EsxCliV2.hardware.pci.list.invoke() + foreach ($Pci in $AllPciDevice) { + # Ignore USB controllers, iLO/iDRAC devices + if ($Pci.DeviceName -like "*USB*" -or $Pci.DeviceName -like "*iLO*" -or $Pci.DeviceName -like "*iDRAC*") { + continue + } + # Get the NICs and storage adapters. + # We found NIC and storage adapters usually have module ID other than 0 or 1 + $pciDevice = [IoDeviceResource]::new() + if ($Pci.ModuleID -ne 0 -and $Pci.ModuleID -ne -1) { + if (!$this.is_pcidevice_exist($Pci)) { + $pciDevice.set_data($Pci, $EsxCliV2) + $this.ComponentResource += $pciDevice + } + } + } + } + + [boolean] is_pcidevice_exist($device) { + foreach ($pci in $this.ComponentResource) { + if ($pci.psobject.TypeNames[0] -eq "IoDeviceResource") { + $vid = [String]::Format("{0:x4}", [int]$device.VendorID) + $did = [String]::Format("{0:x4}", [int]$device.DeviceID) + $svid = [String]::Format("{0:x4}", [int]$device.SubVendorID) + $ssid = [String]::Format("{0:x4}", [int]$device.SubDeviceID) + if ($pci.vid -eq $vid -and $pci.did -eq $did -and + $pci.svid -eq $svid -and $pci.ssid -eq $ssid) { + return $true + } + } + } + return $false + } + + [object] to_jsonobj() { + $Json = $this | Select-Object -Property $this.JsonProperties + $ComponentChildren = @() + $this.ComponentResource | ForEach-Object {$ComponentChildren += $_.to_jsonobj()} + $Json | Add-Member -Name "ComponentResource" -Value $ComponentChildren -MemberType NoteProperty + + return $Json + } + + [string] get_host_status() { + if ($this.powerstatus -and $this.powerstatus -ne 'unknown') { + return $this.powerstatus + } + if ($this.connectionstatus) { + return ("Server " + $this.connectionstatus) + } + else { + return "Server status is unknown" + } + } + + [string] get_prompt_name() { + if ($this.apitype) { + $start = $this.apitype + } + else { + $start = "Host" + } + return $start + " " + $this.hostname + } +} + + +# Class to manage server resources +Class ServerResource { + [string] $type + [string] $model + [string] $vendor + [string] $biosversion + [string] $cpumodel + [string] $cpufeatureid + [string] $uuid + [string] $status + [array] $matchResult + [array] $warnings + [string] $vcgLink + [array] $updateRelease + + [VMware.VimAutomation.Types.VMHost] $vmhost + [Array] $JsonProperties = @('__type__','type', 'model', 'vendor', 'biosversion', + 'cpumodel', 'cpufeatureid', 'uuid','status','matchResult','warnings','vcgLink','updateRelease') + + + [void] set_data( + [VMware.VimAutomation.Types.VMHost] $vmhost) { + $this.vmhost = $vmhost + $this.type = "Server" + $this.model = $this.vmhost.Model + $this.vendor = $this.vmhost.Manufacturer + $this.biosversion = $this.vmhost.ExtensionData.Hardware.BiosInfo.BiosVersion + $this.cpumodel = $this.vmhost.ProcessorType + $cpuFeature = $this.vmhost.ExtensionData.Hardware.CpuFeature + if ($cpuFeature -and $cpuFeature.Count -gt 2) { + $this.cpufeatureid = $this.vmhost.ExtensionData.Hardware.CpuFeature[1].Eax + } + $this.uuid = $this.vmhost.ExtensionData.Hardware.systeminfo.uuid + } + + [object] to_jsonobj() { + return $this | Select-Object -Property $this.JsonProperties + } + +} + +# Class to manage each IO device +Class IoDeviceResource { + + [string] $type + [string] $model + [string] $deviceid + [string] $device + [string] $comptype + [string] $vid + [string] $did + [string] $svid + [string] $ssid + [string] $pciid + [string] $vendor + [string] $driver + [string] $driverversion + [string] $firmware + [string] $status + [array] $matchResult + [array] $warnings + [string] $vcgLink + [array] $updateRelease + + [Array] $JsonProperties = @('__type__','type', 'model', 'deviceid', 'device', + 'comptype', 'vid', 'did', 'svid', 'ssid', 'pciid', + 'vendor', 'driver', 'driverversion', 'firmware','status','matchResult','warnings','vcgLink','updateRelease') + + [void] set_data( + [object] $pci, + [object] $EsxCli) { + $this.type = "IO Device" + $this.model = $Pci.DeviceName + $this.deviceid = $pci.Address + $this.device = $pci.VMKernelName + $this.vid = [String]::Format("{0:x4}", [int]$Pci.VendorID) + $this.did = [String]::Format("{0:x4}", [int]$Pci.DeviceID) + $this.svid = [String]::Format("{0:x4}", [int]$Pci.SubVendorID) + $this.ssid = [String]::Format("{0:x4}", [int]$Pci.SubDeviceID) + $this.pciid = $this.vid + ":" + $this.did + ":" + $this.svid + ":" + $this.ssid + $this.vendor = $pci.VendorName + $this.driver = $Pci.ModuleName + $this.driverversion = "N/A" + $this.firmware = "N/A" + + + # Set component type and driverversion, firmware + if ($this.device -match 'nic') { + $arg = @{} + $arg['nicname'] = $this.device + $nic = $EsxCli.network.nic.get.invoke($arg) + $this.comptype = "Physical NIC" + $this.driverversion = $nic.driverinfo.Version + $this.firmware = $nic.driverinfo.FirmwareVersion + } + elseif ($this.device -match 'hba') { + $arg = @{} + $arg['module'] = $this.driver + $module = $EsxCli.system.module.get.invoke($arg) + $this.comptype = "Storage Adapter" + $this.driverversion = $module.Version + } + } + + [object] to_jsonobj() { + return $this | Select-Object -Property $this.JsonProperties + } + + [string] get_id_detail() { + return $this.driver + " (PCIId:" + $this.pciid + ")" + } +} + +# Class to manage IO device group +Class IoDeviceResourceGroup { + [Array] $iodevices = @() + [Array] $nics = @() + [Array] $adapters = @() + + [void] append_nic([IODeviceResource] $nic) { + $this.iodevices += $nic + $this.nics += $nic + } + + [void] append_storage_adapter([IODeviceResource] $adapter) { + $this.iodevices += $adapter + $this.adapters += $adapter + } + + [boolean] has_nics() { + return $this.nics.Count > 0 + } + + [boolean] has_storage_adapters() { + return $this.adapters.Count > 0 + } + +} + + +# +# Collect hardware inventory data from all the hosts +# +Function Get-VCGHWInfo { + Param( + [Parameter(Mandatory=$true)] $vmHosts + ) + # Collect the hardware data + $Data = @() + foreach($vmHost in $vmHosts) { + $vm = [HostResource]::new($vmHost) + try { + info ("Collecting hardware data from " + $vm.hostname) + $null = $vm.query_components() + if($vm.powerstatus -eq 'poweredOn' -and $vm.connectionstatus -eq 'connected'){ + $Data += $vm + info ("Collecting hardware data from " + $vm.hostname +' success') + } + } + catch { + error ("Failed to collect hardware data from " + $vm.hostname) + } + } + + return $Data +} \ No newline at end of file diff --git a/Modules/VMware.VCGChecker/Get-VCGStatus.ps1 b/Modules/VMware.VCGChecker/Get-VCGStatus.ps1 new file mode 100644 index 0000000..426d6ce --- /dev/null +++ b/Modules/VMware.VCGChecker/Get-VCGStatus.ps1 @@ -0,0 +1,168 @@ +$Uuid = [guid]::NewGuid() +$Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" +$Headers.add("x-request-id", $Uuid) +$Headers.add("x-api-toolid", "180209100001") +$Headers.add("x-api-key", "SJyb8QjK2L") +$Url_Perfix = 'https://apigw.vmware.com/m4/compatibility/v1' +$Url = $Url_Perfix + "/compatible/servers/search?" +$UrlPci = $Url_Perfix + "/compatible/iodevices/search?" +$apiQurryDict=@{} + +# +# Ping remote api server. +# +Function PingApiServer(){ + $apiServerIp='apigw.vmware.com' + $results =Test-Connection $apiServerIp -Quiet + if($results -ne $true){ + error ("Failed to access VMware Compatibility API, + Unable to use comparison function, only view basic hardware information; + you can use 'Get-VCGHWInfo -g ' create hardware json, + then use 'Check-VCGStatus -f ' load hardware json file to comapre when connect an available network") + Exit(1) + } +} + +# +# Get the web request. +# +Function Get-WebRequest($VCGurl) { + try { + $req = Invoke-WebRequest -Headers $Headers -Uri $VCGUrl -ErrorVariable $err -UseBasicParsing + } + catch { + if ($err[0].errorrecord.exception.response) { + error ("WebReponse code:" + $err[0].errorrecord.exception.response.statuscode.value__) + error ($exitScript) + Exit(1) + } + else { + error ("Failed to check " + $type + " data for " + $HostResource.hostname) + error ("Failed to access VMware Compatibility API, please check your Internet connection or contact VMware Compatibility API administrator") + error ("Exit the script") + Exit(1) + } + } + return $req +} + +Function Get-RemoteApiTitleString([object]$device,$EsxiVersion){ + if ($device.type -eq 'Server') { + $Title = $device.model + $device.vendor + $device.cpufeatureid + $device.biosversion +$EsxiVersion + } + else{ + $Title = $device.vid + $device.did + $device.Svid + $device.Ssid + $EsxiVersion + } + return $Title +} + +Function Get-ResponseFromApi([object]$device,$EsxiVersion){ + if ($device.type -eq 'Server') { + $VCGUrl = $Url + "model=" + $device.model + "&releaseversion=" + $EsxiVersion ` + + "&vendor=" + $device.vendor + "&cpuFeatureId=" + $device.cpufeatureid ` + + "&bios=" + $device.biosversion + debug ("Model:" + $device.model) + debug ("VCG Url:" + $VCGUrl) + $Headers.GetEnumerator() | ForEach-Object {debug ("Req Header:" + $_.key + ":" + $_.value)} + $request = Get-WebRequest $VCGUrl + $Response = ConvertFrom-Json -InputObject $request -Erroraction 'silentlycontinue' + } + elseif ($device.type -eq 'IO Device') { + $VCGUrl = $UrlPci + "vid=0X" + $device.vid + "&did=0X" + $device.did + "&svid=0X" + $device.Svid ` + + "&ssid=0X" + $device.Ssid + "&releaseversion=" + $EsxiVersion ` + + "&driver=" + $device.Driver + "&driverversion=" + $device.driverversion + "&firmware=N/A" + debug ("Model:" + $device.model) + debug ("VCG Url:" + $VCGUrl) + $Headers.GetEnumerator() | ForEach-Object {debug ("Req Header:" + $_.key + ":" + $_.value)} + $request = Get-WebRequest $VCGUrl + $Response = ConvertFrom-Json -InputObject $request -Erroraction 'silentlycontinue' + } + return $Response +} +# +# Get the data from api +# +Function Get-VCGData($HostResource) { + foreach ($device in $HostResource.ComponentResource) { + if ($HostResource.checkRelease) { + $EsxiVersion = $HostResource.checkRelease + } + else { + $EsxiVersion = $HostResource.version + } + $temp=0 + $title=Get-RemoteApiTitleString $device $EsxiVersion + if($apiQurryDict.Count -eq 0){ + $Response= Get-ResponseFromApi $device $EsxiVersion + $apiQurryDict.Add($title,$Response) + }else{ + foreach($onetitle in $apiQurryDict.keys){ + if($onetitle -eq $title){ + $Response= $apiQurryDict[$onetitle] + $temp=1 + break + } + } + if($temp -eq 0){ + $Response= Get-ResponseFromApi $device $EsxiVersion + $apiQurryDict.Add($title,$Response) + } + } + + if ($Response.matches) { + foreach ($match in $Response.matches) { + $device.vcgLink += [string]$match.vcgLink + } + } + else { + foreach ($potentialMatche in $Response.potentialMatches) { + $device.vcgLink += [string]$potentialMatche.vcgLink + } + } + $device.status = [string]$Response.searchResult.status + $device.matchResult = [string]$Response.searchResult.matchResult + $device.warnings = $Response.searchResult.warnings + $device.updateRelease = [string]$Response.searchOption.foundRelease + } +} + +# +# Send the hardware data to VCG API and handle returned result +# +Function Get-DataFromRemoteApi([object]$servers) { + info ("Checking hardware compatibility result with VMware Compatibility Guide API...") + info ("This may take a few minutes depending on your network.") + for ($idx = 0; $idx -lt $servers.Count; $idx++) { + $server = $servers[$idx] + $i = $idx + 1 + info ([string]$i + "/" + [string]$servers.Count + " - Checking hardware compatibility results for " + $server.hostname) + if (!$server -or $server.ComponentResource.Count -eq 0) { + error('Failed to get the hardware info.') + Exit(1) + } + Get-VCGData $server + } + return $servers +} + +Function Get-VCGStatus{ + Param( + [Parameter(Mandatory=$true)] $Data, + [Parameter(Mandatory=$false)] $Version + ) + $checkRelease = $Version + PingApiServer + + foreach ($vmHost in $Data) { + # $vmHost|add-member -Name "checkRelease" -value $checkRelease -MemberType NoteProperty -Force + $vmHost.checkRelease=$checkRelease + } + + $results = Get-DataFromRemoteApi($Data) + if ($results.Count -eq 0) { + error ("Failed to get compatibility results. No report will be generated") + error ("Exit the script") + Exit(1) + } + return $results +} \ No newline at end of file diff --git a/Modules/VMware.VCGChecker/README.md b/Modules/VMware.VCGChecker/README.md new file mode 100644 index 0000000..79007d4 --- /dev/null +++ b/Modules/VMware.VCGChecker/README.md @@ -0,0 +1,41 @@ +# About +This module is designed to ease the work of collecting hardware inventory data and compare it with VMware's official VCG data. Before deploying vSphere, it is required to validate your physical machines with VCG to make sure all the devices in your physical machines are compatible with the vSphere version you want to install. It is a time-consuming and painful experience to collect hardware/driver/firmware data from all of the machines, especially when you have a huge number of machines in your data center. + +# How It Works +By using this module, it will automate the data collection and comparison work. + +When running the module, it will connect to the target vCenter or ESXi to read the hardware data. It is a read-only operation and nothing on the target hosts will be changed. There is almost no impact on the machine's performance as the read operation takes just few seconds. + +The module will then send your hardware inventory data to VMware's offical website to conduct a compatibility check. The result will be 'Compatible', 'May not be compatible' or 'Not compatible'. +* Compatible: the hardware is compatible with the given vSphere release. Link to that VCG page will be provided. +* May not be compatible: manual check is required to confirm the compatibility status of this hardware. A few potential matching VCG records will be provided. +* Not compatible: the hardware is not compatible with the given vSphere release. + +After the checking is completed, the module will generate reports in different formats for your review and future use. A summary view in html will give you an overview of your machines compatibility status; an html file with device details to view each device/driver/firmware you have, their compatibility with vSphere version you specified and links to the corresponding VCG documents online; a csv file with device details to allow customization on report in Excel or by your own tool. + +# Usage +Considering many data center may have control on internet access, we create 3 cmdlets to meet various situations. +* Get-VCGHWInfo: cmdlet to collect hardware info +* Get-VCGStatus: cmdlet to check hardware compatibility by query VCG website +* Export-VCGReport: cmdlet to export the summary/html/csv reports + +1. You need to first import this module after you import PowerCLI module +PS> Import-Module <path_to_VMware.VCGChecker.psd1> + +2. Connect to the target vSphere hosts using Connect-VIServer and get VMHosts +PS> Connect-VIServer -Server <server> -User <username> -Password <password> +PS> $vmhosts = Get-VMHost + +3. Collect the hardware data +PS> $hwdata = Get-VCGHWInfo -vmHosts $vmhosts +Note: if you don't have internet access, you need to connect your client to internet before proceeding to the next step. + +4. Specify the target vSphere release you want to check and submit the hardware data to VMware website +PS> $vcgdata= Get-VCGStatus -Data $hwdata -Version '<release>' + +5. Save the compatibility reports +PS> Export-VCGReport -Data $vcgdata -Dir <dir> + +# Known Limitation +* The module is not able to get the firmware version for HBA devices. +* The module is not able to get the HDD/SSD data. diff --git a/Modules/VMware.VCGChecker/Save-VCGJsonFile.ps1 b/Modules/VMware.VCGChecker/Save-VCGJsonFile.ps1 new file mode 100644 index 0000000..785fc00 --- /dev/null +++ b/Modules/VMware.VCGChecker/Save-VCGJsonFile.ps1 @@ -0,0 +1,17 @@ +Function Save-VCGJsonFile{ + Param( + [Parameter(Mandatory=$true)] $FileName, + [Parameter(Mandatory=$true)] $Data, + [Parameter(Mandatory=$true)] $Dir + ) + $json = @() + $Data | ForEach-Object { $json += $_.to_jsonobj()} + + if (!(Test-Path $Dir)) { + New-Item -Type directory -Confirm:$false -Path $Dir -Force |Out-Null + } + + $Path= $Dir + '\' + $FileName + '.json' + info ("Saving data to " + $Path) + ConvertTo-Json -Depth 10 -Compress $json | Out-File -encoding 'UTF8' -FilePath $Path +} \ No newline at end of file diff --git a/Modules/VMware.VCGChecker/VMware.VCGChecker.psd1 b/Modules/VMware.VCGChecker/VMware.VCGChecker.psd1 new file mode 100644 index 0000000..12493af --- /dev/null +++ b/Modules/VMware.VCGChecker/VMware.VCGChecker.psd1 @@ -0,0 +1,90 @@ +# +# Module manifest for module 'VMware.VCGChecker' +# +# Generated by: fdai@vmware.com, zhangta@vmware.com, linweij@vmware.com +# +# Generated on: 11/15/18 +# + +@{ + + # Script module or binary module file associated with this manifest. + # RootModule = 'VMware.VCGChecker.psm1' + + # Version number of this module. + ModuleVersion = '1.0.0' + + # Supported PSEditions + # CompatiblePSEditions = @() + + # ID used to uniquely identify this module + # GUID = '' + + # Author of this module + Author = 'Frank Dai, Tao Zhang, Linwei Jiang' + + # Company or vendor of this module + CompanyName = 'VMware' + + # Copyright statement for this module + Copyright = '(c) 2018 VMware. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'PowerShell Module for Checking Hardware Compatibility Status' + + RequiredModules = @('VMware.VimAutomation.Core') + + # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess + NestedModules = 'Get-VCGHWInfo.ps1','Get-VCGStatus.ps1','Export-VCGReport.ps1', 'Save-VCGJsonFile.ps1', 'logger.ps1' + + # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. + FunctionsToExport = 'Get-VCGHWInfo', 'Get-VCGStatus', 'Export-VCGReport', 'Save-VCGJsonFile' + # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. + CmdletsToExport = @() + + # Variables to export from this module + VariablesToExport = '*' + + # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. + AliasesToExport = @() + + # DSC resources to export from this module + # DscResourcesToExport = @() + + # List of all modules packaged with this module + # ModuleList = @() + + # List of all files packaged with this module + # FileList = @() + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + # ProjectUri = '' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + } # End of PSData hashtable + + } # End of PrivateData hashtable + + # HelpInfo URI of this module + # HelpInfoURI = '' + + # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. + # DefaultCommandPrefix = '' + + } \ No newline at end of file diff --git a/Modules/VMware.VCGChecker/logger.ps1 b/Modules/VMware.VCGChecker/logger.ps1 new file mode 100644 index 0000000..686611a --- /dev/null +++ b/Modules/VMware.VCGChecker/logger.ps1 @@ -0,0 +1,112 @@ +<# +Copyright 2018 VMware, Inc. All rights reserved. + +#> + +# Messages +$HEADER_OK = "[OK] " +$HEADER_INFO = "[INFO] " +$HEADER_WARNING = "[WARNING] " +$HEADER_ERR = "[ERROR] " + +Class DebugLog +{ + # Static variables of the logger class + static [string] $CAFILE_PATH = "./.certs/" + + [boolean] $debug + [string] $logfile + + DebugLog() + { + $this.debug = $false + $this.logfile = $null + if (!(Test-Path $this::CAFILE_PATH)) + { + New-Item -Type directory -Confirm:$false -Path $this::CAFILE_PATH + } + } + + [void] SetDebug( + [boolean] $debug, + [string] $hostname + ){ + if (!$hostname) {$hostname = ''} + $this.debug = $debug + if ($this.debug) + { + $this.logfile = $this::CAFILE_PATH + $hostname + [DateTime]::Now.ToString("_yyyy-MM-dd_HH-mm") + ".log" + }else{ + $this.logfile = $null + } + } + + [void] log_vars( + [string] $message, + [object] $var + ){ + $this.log($message + $var) + } + + [void] log( + [string] $message + ){ + if (!$this.debug -or !$this.logfile) {return} + try + { + $message | Out-File $this.logfile -Append + }catch { + Out-Host -InputObject ("[Exception] Failed to write to a logfile: " + $this.logfile) + Out-Host -InputObject $_ + } + } +} + +Function debug_vars( + [string] $message, + [object] $var) +{ + $logger.log_vars($message, $var) +} + +Function debug( + [string] $message) +{ + $logger.log($message) +} + +Function vcglog( + [string] $message, + [string] $header="") +{ + $msg = $header + $message + $logger.log($msg) + Out-Host -InputObject $msg +} + +Function ok( + [string] $message) +{ + vcglog $message $HEADER_OK +} + +Function warning( + [string] $message) +{ + vcglog $message $HEADER_WARNING +} + +Function info( + [string] $message) +{ + vcglog $message $HEADER_INFO +} + +Function error( + [string] $message) +{ + vcglog $message $HEADER_ERR +} + +$logger = [DebugLog]::new() +$logger.SetDebug($true, "vcc-debug") \ No newline at end of file diff --git a/Modules/VMware.VMC.NSXT/VMware.VMC.NSXT.psd1 b/Modules/VMware.VMC.NSXT/VMware.VMC.NSXT.psd1 new file mode 100644 index 0000000..9eca367 --- /dev/null +++ b/Modules/VMware.VMC.NSXT/VMware.VMC.NSXT.psd1 @@ -0,0 +1,88 @@ +# +# Module manifest for module 'VMware.VMC.NSXT' +# +# Generated by: wlam@vmware.com +# +# Generated on: 09/11/18 +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'VMware.VMC.NSXT.psm1' + +# Version number of this module. +ModuleVersion = '1.0.0' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = 'c094608a-7480-4751-a14c-c9dd68870607' + +# Author of this module +Author = 'William Lam' + +# Company or vendor of this module +CompanyName = 'VMware' + +# Copyright statement for this module +Copyright = '(c) 2018 VMware. All rights reserved.' + +# Description of the functionality provided by this module +Description = 'PowerShell Module for Managing NSX-T on VMware Cloud on AWS' + +# Minimum version of the Windows PowerShell engine required by this module +PowerShellVersion = '6.0' + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = 'Connect-NSXTProxy', 'Get-NSXTSegment', 'New-NSXTSegment', 'Remove-NSXTSegment', 'Get-NSXTGroup', 'New-NSXTGroup', 'Remove-NSXTGroup', 'Get-NSXTService', 'New-NSXTService', 'Get-NSXTFirewall', 'New-NSXTFirewall', 'Remove-NSXTFirewall' +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = @() + +# Variables to export from this module +VariablesToExport = '*' + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = @() + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + # ProjectUri = '' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} \ No newline at end of file diff --git a/Modules/VMware.VMC.NSXT/VMware.VMC.NSXT.psm1 b/Modules/VMware.VMC.NSXT/VMware.VMC.NSXT.psm1 new file mode 100644 index 0000000..3bfdfe3 --- /dev/null +++ b/Modules/VMware.VMC.NSXT/VMware.VMC.NSXT.psm1 @@ -0,0 +1,889 @@ +Function Connect-NSXTProxy { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/11/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Retrieves NSX-T Proxy URL + acquire CSP Access Token to then be used with NSXT-T Policy API + .DESCRIPTION + This cmdlet creates $global:nsxtProxyConnection object containing the NSX-T Proxy URL along with CSP Token + .EXAMPLE + Connect-NSXTProxy -RefreshToken $RefreshToken -OrgName $OrgName -SDDCName $SDDCName + .NOTES + You must be logged into VMC using Connect-VmcServer cmdlet +#> + Param ( + [Parameter(Mandatory=$true)][String]$RefreshToken, + [Parameter(Mandatory=$true)][String]$OrgName, + [Parameter(Mandatory=$true)][String]$SDDCName + ) + + If (-Not $global:DefaultVMCServers.IsConnected) { Write-error "No valid VMC Connection found, please use the Connect-VMC to connect"; break } Else { + $sddcService = Get-VmcService "com.vmware.vmc.orgs.sddcs" + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + $sddc = $sddcService.get($orgId,$sddcId) + if($sddc.resource_config.nsxt) { + $nsxtProxyURL = $sddc.resource_config.nsx_api_public_endpoint_url + } else { + Write-Host -ForegroundColor Red "This is not an NSX-T based SDDC" + break + } + } + + $results = Invoke-WebRequest -Uri "https://console.cloud.vmware.com/csp/gateway/am/api/auth/api-tokens/authorize?refresh_token=$RefreshToken" -Method POST -ContentType "application/json" -UseBasicParsing -Headers @{"csp-auth-token"="$RefreshToken"} + if($results.StatusCode -ne 200) { + Write-Host -ForegroundColor Red "Failed to retrieve Access Token, please ensure your VMC Refresh Token is valid and try again" + break + } + $accessToken = ($results | ConvertFrom-Json).access_token + + $headers = @{ + "csp-auth-token"="$accessToken" + "Content-Type"="application/json" + "Accept"="application/json" + } + $global:nsxtProxyConnection = new-object PSObject -Property @{ + 'Server' = $nsxtProxyURL + 'headers' = $headers + } + $global:nsxtProxyConnection +} + +Function Get-NSXTSegment { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/11/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Returns all NSX-T Segments (Logical Networks) + .DESCRIPTION + This cmdlet retrieves all NSX-T Segments (Logical Networks) + .EXAMPLE + Get-NSXTSegment + .EXAMPLE + Get-NSXTSegment -Name "sddc-cgw-network-1" +#> + Param ( + [Parameter(Mandatory=$False)]$Name, + [Switch]$Troubleshoot + ) + + If (-Not $global:nsxtProxyConnection) { Write-error "No NSX-T Proxy Connection found, please use Connect-NSXTProxy" } Else { + $method = "GET" + $segmentsURL = $global:nsxtProxyConnection.Server + "/policy/api/v1/infra/networks/cgw/segments" + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $METHOD`n$segmentsURL`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $segmentsURL -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $segmentsURL -Method $method -Headers $global:nsxtProxyConnection.headers + } + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + + if($requests.StatusCode -eq 200) { + $segments = ($requests.Content | ConvertFrom-Json).results + + if ($PSBoundParameters.ContainsKey("Name")){ + $segments = $segments | where {$_.display_name -eq $Name} + } + + $results = @() + foreach ($segment in $segments) { + + $subnets = $segment.subnets + $network = $subnets.network + $gateway = $subnets.gateway_addresses + $dhcpRange = $subnets.dhcp_ranges + + $tmp = [pscustomobject] @{ + Name = $segment.display_name; + ID = $segment.Id; + Network = $network; + Gateway = $gateway; + DHCPRange = $dhcpRange; + } + $results+=$tmp + } + $results + } else { + Write-Error "Failed to retrieve NSX-T Segments" + } + } +} + +Function New-NSXTSegment { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/11/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Creates a new NSX-T Segment (Logical Networks) + .DESCRIPTION + This cmdlet creates a new NSX-T Segment (Logical Networks) + .EXAMPLE + New-NSXTSegment -Name "sddc-cgw-network-4" -Gateway "192.168.4.1" -Prefix "24" -DHCP -DHCPRange "192.168.4.2-192.168.4.254" +#> + Param ( + [Parameter(Mandatory=$True)]$Name, + [Parameter(Mandatory=$True)]$Gateway, + [Parameter(Mandatory=$True)]$Prefix, + [Parameter(Mandatory=$False)]$DHCPRange, + [Switch]$DHCP, + [Switch]$Troubleshoot + ) + + If (-Not $global:nsxtProxyConnection) { Write-error "No NSX-T Proxy Connection found, please use Connect-NSXTProxy" } Else { + if($DHCP) { + $dhcpConf = @($DHCPRange) + } else { + $dhcpConf = @($null) + } + + $subnets = @{ + gateway_addresses = @($gateway); + prefix_len = $Prefix; + dhcp_ranges = $dhcpConf + } + + $payload = @{ + display_name = $Name; + subnets = @($subnets) + } + $body = $payload | ConvertTo-Json -depth 4 + + $method = "PUT" + $newSegmentsURL = $global:nsxtProxyConnection.Server + "/policy/api/v1/infra/networks/cgw/segments/$Name" + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$newSegmentsURL`n" + Write-Host -ForegroundColor cyan "[DEBUG]`n$body`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $newSegmentsURL -Body $body -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $newSegmentsURL -Body $body -Method $method -Headers $global:nsxtProxyConnection.headers + } + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + + if($requests.StatusCode -eq 200) { + Write-Host "Succesfully created new NSX-T Segment $Name" + ($requests.Content | ConvertFrom-Json) | select display_name, id + } else { + Write-Error "Failed to create new NSX-T Segment" + + } + } +} + +Function Remove-NSXTSegment { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/11/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Removes an NSX-T Segment (Logical Networks) + .DESCRIPTION + This cmdlet removes an NSX-T Segment (Logical Networks) + .EXAMPLE + Remove-NSXTSegment -Id "sddc-cgw-network-4" +#> + Param ( + [Parameter(Mandatory=$True)]$Id, + [Switch]$Troubleshoot + ) + + If (-Not $global:nsxtProxyConnection) { Write-error "No NSX-T Proxy Connection found, please use Connect-NSXTProxy" } Else { + $method = "DELETE" + $deleteSegmentsURL = $global:nsxtProxyConnection.Server + "/policy/api/v1/infra/networks/cgw/segments/$Id" + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$deleteSegmentsURL`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $deleteSegmentsURL -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $deleteSegmentsURL -Method $method -Headers $global:nsxtProxyConnection.headers + } + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + + if($requests.StatusCode -eq 200) { + Write-Host "Succesfully removed NSX-T Segment $Name" + } else { + Write-Error "Failed to remove NSX-T Segments" + + } + } +} + +Function Get-NSXTFirewall { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/11/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Returns all NSX-T Firewall Rules on MGW or CGW + .DESCRIPTION + This cmdlet retrieves all NSX-T Firewall Rules on MGW or CGW + .EXAMPLE + Get-NSXTFirewall -GatewayType MGW + .EXAMPLE + Get-NSXTFirewall -GatewayType MGW -Name "Test" +#> + param( + [Parameter(Mandatory=$false)][String]$Name, + [Parameter(Mandatory=$true)][ValidateSet("MGW","CGW")][String]$GatewayType, + [Switch]$Troubleshoot + ) + + If (-Not $global:nsxtProxyConnection) { Write-error "No NSX-T Proxy Connection found, please use Connect-NSXTProxy" } Else { + $method = "GET" + $edgeFirewallURL = $global:nsxtProxyConnection.Server + "/policy/api/v1/infra/domains/$($GatewayType.toLower())/edge-communication-maps/default" + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$edgeFirewallURL`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $edgeFirewallURL -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $edgeFirewallURL -Method $method -Headers $global:nsxtProxyConnection.headers + } + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + + if($requests.StatusCode -eq 200) { + $rules = ($requests.Content | ConvertFrom-Json).communication_entries + + if ($PSBoundParameters.ContainsKey("Name")){ + $rules = $rules | where {$_.display_name -eq $Name} + } + + $results = @() + foreach ($rule in $rules | Sort-Object -Property sequence_number) { + $sourceGroups = $rule.source_groups + $source = @() + foreach ($sourceGroup in $sourceGroups) { + if($sourceGroup -eq "ANY") { + $source += $sourceGroup + break + } else { + $sourceGroupURL = $global:nsxtProxyConnection.Server + "/policy/api/v1" + $sourceGroup + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$sourceGroupURL`n" + } + try { + $requests = Invoke-WebRequest -Uri $sourceGroupURL -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + $group = ($requests.Content | ConvertFrom-Json) + $source += $group.display_name + } + } + + $destinationGroups = $rule.destination_groups + $destination = @() + foreach ($destinationGroup in $destinationGroups) { + if($destinationGroup -eq "ANY") { + $destination += $destinationGroup + break + } else { + $destionationGroupURL = $global:nsxtProxyConnection.Server + "/policy/api/v1" + $destinationGroup + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$destionationGroupURL`n" + } + try { + $requests = Invoke-WebRequest -Uri $destionationGroupURL -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + $group = ($requests.Content | ConvertFrom-Json) + $destination += $group.display_name + } + } + + $serviceGroups = $rule.services + $service = @() + foreach ($serviceGroup in $serviceGroups) { + if($serviceGroup -eq "ANY") { + $service += $serviceGroup + break + } else { + $serviceGroupURL = $global:nsxtProxyConnection.Server + "/policy/api/v1" + $serviceGroup + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$serviceGroupURL`n" + } + try { + $requests = Invoke-WebRequest -Uri $serviceGroupURL -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + $group = ($requests.Content | ConvertFrom-Json) + $service += $group.display_name + } + } + + $tmp = [pscustomobject] @{ + SequenceNumber = $rule.sequence_number; + Name = $rule.display_name; + ID = $rule.id; + Source = $source; + Destination = $destination; + Services = $service; + Action = $rule.action; + } + $results+=$tmp + } + $results + + } else { + Write-Error "Failed to retrieve NSX-T Firewall Rules" + } + } +} + +Function New-NSXTFirewall { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/11/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Creates a new NSX-T Firewall Rule on MGW or CGW + .DESCRIPTION + This cmdlet creates a new NSX-T Firewall Rule on MGW or CGW + .EXAMPLE + New-NSXTFirewall -GatewayType MGW -Name TEST -Id TEST -SourceGroupId ESXI -DestinationGroupId ANY -Service ANY -Logged $true -SequenceNumber 7 -Action ALLOW +#> + Param ( + [Parameter(Mandatory=$True)]$Name, + [Parameter(Mandatory=$true)][ValidateSet("MGW","CGW")][String]$GatewayType, + [Parameter(Mandatory=$True)]$Id, + [Parameter(Mandatory=$True)]$SequenceNumber, + [Parameter(Mandatory=$True)]$SourceGroupId, + [Parameter(Mandatory=$True)]$DestinationGroupId, + [Parameter(Mandatory=$True)]$Service, + [Parameter(Mandatory=$True)][ValidateSet("ALLOW","DENY")]$Action, + [Parameter(Mandatory=$false)][Boolean]$Logged=$false, + [Switch]$Troubleshoot + ) + + If (-Not $global:nsxtProxyConnection) { Write-error "No NSX-T Proxy Connection found, please use Connect-NSXTProxy" } Else { + + if($DestinationGroupId -eq "ANY") { + $destinationGroups = $DestinationGroupId + } else { + $destinationGroups = "/infra/domains/$($GatewayType.toLower())/groups/$DestinationGroupId" + } + + $sourceGroups = @() + foreach ($group in $SourceGroupId) { + $tmp = "/infra/domains/$($GatewayType.toLower())/groups/$group" + $sourceGroups+= $tmp + } + + $services = @() + foreach ($serviceName in $Service) { + if($serviceName -eq "ANY") { + $tmp = "ANY" + } else { + $tmp = "/infra/services/$serviceName" + } + $services+=$tmp + } + + $payload = @{ + display_name = $Name; + resource_type = "CommunicationEntry"; + id = $Id; + sequence_number = $SequenceNumber; + destination_groups = @($destinationGroups); + source_groups = $sourceGroups; + logged = $Logged; + scope = @("/infra/labels/$($GatewayType.toLower())"); + services = $services; + action = $Action; + } + + $body = $payload | ConvertTo-Json -depth 5 + + $method = "PUT" + $newFirewallURL = $global:nsxtProxyConnection.Server + "/policy/api/v1/infra/domains/$($GatewayType.toLower())/edge-communication-maps/default/communication-entries/$Id" + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$newFirewallURL`n" + Write-Host -ForegroundColor cyan "[DEBUG]`n$body`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $newFirewallURL -Body $body -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $newFirewallURL -Body $body -Method $method -Headers $global:nsxtProxyConnection.headers + } + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + + if($requests.StatusCode -eq 200) { + Write-Host "Succesfully created new NSX-T Firewall Rule $Name" + ($requests.Content | ConvertFrom-Json) | select display_name, id + } else { + Write-Error "Failed to create new NSX-T Firewall Rule" + } + } +} + +Function Remove-NSXTFirewall { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/11/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Removes an NSX-T Firewall Rule on MGW or CGW + .DESCRIPTION + This cmdlet removes an NSX-T Firewall Rule on MGW or CGW + .EXAMPLE + Remove-NSXTFirewall -Id TEST -GatewayType MGW -Troubleshoot +#> + Param ( + [Parameter(Mandatory=$True)]$Id, + [Parameter(Mandatory=$true)][ValidateSet("MGW","CGW")][String]$GatewayType, + [Switch]$Troubleshoot + ) + + If (-Not $global:nsxtProxyConnection) { Write-error "No NSX-T Proxy Connection found, please use Connect-NSXTProxy" } Else { + $method = "DELETE" + $deleteGgroupURL = $global:nsxtProxyConnection.Server + "/policy/api/v1/infra/domains/$($GatewayType.toLower())/edge-communication-maps/default/communication-entries/$Id" + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$deleteGgroupURL`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $deleteGgroupURL -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $deleteGgroupURL -Method $method -Headers $global:nsxtProxyConnection.headers + } + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + + if($requests.StatusCode -eq 200) { + Write-Host "Succesfully removed NSX-T Firewall Rule $Name" + } else { + Write-Error "Failed to create new NSX-T Firewall Rule" + } + } +} + +Function Get-NSXTGroup { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/11/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Returns all NSX-T Groups for MGW or CGW + .DESCRIPTION + This cmdlet retrieves all NSX-T Groups for MGW or CGW + .EXAMPLE + Get-NSXTGroup -GatewayType MGW + .EXAMPLE + Get-NSXTGroup -GatewayType MGW -Name "Test" +#> + param( + [Parameter(Mandatory=$false)][String]$Name, + [Parameter(Mandatory=$true)][ValidateSet("MGW","CGW")][String]$GatewayType, + [Switch]$Troubleshoot + ) + + If (-Not $global:nsxtProxyConnection) { Write-error "No NSX-T Proxy Connection found, please use Connect-NSXTProxy" } Else { + $method = "GET" + $edgeFirewallGroupsURL = $global:nsxtProxyConnection.Server + "/policy/api/v1/infra/domains/$($GatewayType.toLower())/groups" + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$edgeFirewallGroupsURL`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $edgeFirewallGroupsURL -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $edgeFirewallGroupsURL -Method $method -Headers $global:nsxtProxyConnection.headers + } + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + + if($requests.StatusCode -eq 200) { + $groups = ($requests.Content | ConvertFrom-Json).results + + if ($PSBoundParameters.ContainsKey("Name")){ + $groups = $groups | where {$_.display_name -eq $Name} + } + + $results = @() + foreach ($group in $groups) { + if($group.tags.tag -eq $null) { + $groupType = "USER_DEFINED" + } else { $groupType = $group.tags.tag } + + $members = @() + foreach ($member in $group.expression) { + if($member.ip_addresses) { + $members += $member.ip_addresses + } else { + if($member.resource_type -eq "Condition") { + $members += $member.value + } + } + } + + $tmp = [pscustomobject] @{ + Name = $group.display_name; + ID = $group.id; + Type = $groupType; + Members = $members; + } + $results+=$tmp + } + $results + } else { + Write-Error "Failed to retrieve NSX-T Groups" + } + } +} + +Function New-NSXTGroup { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/11/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Creates a new NSX-T Group on MGW or CGW + .DESCRIPTION + This cmdlet creates a new NSX-T Firewall Rule on MGW or CGW + .EXAMPLE + New-NSXTGroup -GatewayType MGW -Name Foo -IPAddress @("172.31.0.0/24") +#> + Param ( + [Parameter(Mandatory=$True)]$Name, + [Parameter(Mandatory=$true)][ValidateSet("MGW","CGW")][String]$GatewayType, + [Parameter(Mandatory=$True)][String[]]$IPAddress, + [Switch]$Troubleshoot + ) + + If (-Not $global:nsxtProxyConnection) { Write-error "No NSX-T Proxy Connection found, please use Connect-NSXTProxy" } Else { + $expression = @{ + resource_type = "IPAddressExpression"; + ip_addresses = $IPAddress; + } + + $payload = @{ + display_name = $Name; + expression = @($expression); + } + $body = $payload | ConvertTo-Json -depth 5 + + $method = "PUT" + $newGroupURL = $global:nsxtProxyConnection.Server + "/policy/api/v1/infra/domains/$($GatewayType.toLower())/groups/$Name" + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$newGroupURL`n" + Write-Host -ForegroundColor cyan "[DEBUG]`n$body`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $newGroupURL -Body $body -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $newGroupURL -Body $body -Method $method -Headers $global:nsxtProxyConnection.headers + } + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + + if($requests.StatusCode -eq 200) { + Write-Host "Succesfully created new NSX-T Group $Name" + ($requests.Content | ConvertFrom-Json) | select display_name, id + } else { + Write-Error "Failed to create new NSX-T Group" + } + } +} + +Function Remove-NSXTGroup { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/11/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Removes an NSX-T Group + .DESCRIPTION + This cmdlet removes an NSX-T Group + .EXAMPLE + Remove-NSXTGroup -Id Foo -GatewayType MGW -Troubleshoot +#> + Param ( + [Parameter(Mandatory=$True)]$Id, + [Parameter(Mandatory=$true)][ValidateSet("MGW","CGW")][String]$GatewayType, + [Switch]$Troubleshoot + ) + + If (-Not $global:nsxtProxyConnection) { Write-error "No NSX-T Proxy Connection found, please use Connect-NSXTProxy" } Else { + $method = "DELETE" + $deleteGgroupURL = $global:nsxtProxyConnection.Server + "/policy/api/v1/infra/domains/$($GatewayType.toLower())/groups/$Id" + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$deleteGgroupURL`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $deleteGgroupURL -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $deleteGgroupURL -Method $method -Headers $global:nsxtProxyConnection.headers + } + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + + if($requests.StatusCode -eq 200) { + Write-Host "Succesfully removed NSX-T Group $Name" + } else { + Write-Error "Failed to create new NSX-T Group" + } + } +} + +Function Get-NSXTService { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/11/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Returns all NSX-T Services + .DESCRIPTION + This cmdlet retrieves all NSX-T Services + .EXAMPLE + Get-NSXTService + .EXAMPLE + Get-NSXTService -Name "WINS" +#> + param( + [Parameter(Mandatory=$false)][String]$Name, + [Switch]$Troubleshoot + ) + + If (-Not $global:nsxtProxyConnection) { Write-error "No NSX-T Proxy Connection found, please use Connect-NSXTProxy" } Else { + $method = "GET" + $serviceGroupsURL = $global:nsxtProxyConnection.Server + "/policy/api/v1/infra/services" + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$serviceGroupsURL`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $serviceGroupsURL -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $serviceGroupsURL -Method $method -Headers $global:nsxtProxyConnection.headers + } + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + + if($requests.StatusCode -eq 200) { + $services = ($requests.Content | ConvertFrom-Json).results + + if ($PSBoundParameters.ContainsKey("Name")){ + $services = $services | where {$_.display_name -eq $Name} + } + + $results = @() + foreach ($service in $services | Sort-Object -Propert display_name) { + $serviceEntry = $service.service_entries + $serviceProtocol = $serviceEntry.l4_protocol + $serviceSourcePorts = $serviceEntry.source_ports + $serviceDestinationPorts = $serviceEntry.destination_ports + + $tmp = [pscustomobject] @{ + Name = $service.display_name; + Id = $service.id; + Protocol = $serviceProtocol; + Source = $serviceSourcePorts; + Destination = $serviceDestinationPorts; + } + $results += $tmp + } + $results + } else { + Write-Error "Failed to retrieve NSX-T Services" + } + } +} + +Function New-NSXTService { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/11/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Creates a new NSX-T Service + .DESCRIPTION + This cmdlet creates a new NSX-T Service + .EXAMPLE + New-NSXTService -Name "MyHTTP2" -Protocol TCP -DestinationPorts @("8080","8081") +#> + Param ( + [Parameter(Mandatory=$True)]$Name, + [Parameter(Mandatory=$True)][String[]]$DestinationPorts, + [Parameter(Mandatory=$True)][ValidateSet("TCP","UDP")][String]$Protocol, + [Switch]$Troubleshoot + ) + + If (-Not $global:nsxtProxyConnection) { Write-error "No NSX-T Proxy Connection found, please use Connect-NSXTProxy" } Else { + $serviceEntry = @() + $entry = @{ + display_name = $name + "-$destinationPort" + resource_type = "L4PortSetServiceEntry"; + destination_ports = @($DestinationPorts); + l4_protocol = $Protocol; + } + $serviceEntry+=$entry + + $payload = @{ + display_name = $Name; + service_entries = $serviceEntry; + } + $body = $payload | ConvertTo-Json -depth 5 + + $method = "PUT" + $newServiceURL = $global:nsxtProxyConnection.Server + "/policy/api/v1/infra/services/$Name" + + if($Troubleshoot) { + Write-Host -ForegroundColor cyan "`n[DEBUG] - $method`n$newServiceURL`n" + Write-Host -ForegroundColor cyan "[DEBUG]`n$body`n" + } + + try { + if($PSVersionTable.PSEdition -eq "Core") { + $requests = Invoke-WebRequest -Uri $newServiceURL -Body $body -Method $method -Headers $global:nsxtProxyConnection.headers -SkipCertificateCheck + } else { + $requests = Invoke-WebRequest -Uri $newServiceURL -Body $body -Method $method -Headers $global:nsxtProxyConnection.headers + } + } catch { + Write-Host -ForegroundColor Red "`nThe NSX-T Proxy session is no longer valid, please re-run the Connect-NSXTProxy cmdlet to retrieve a new token`n" + break + } + + if($requests.StatusCode -eq 200) { + Write-Host "Succesfully created new NSX-T Service $Name" + ($requests.Content | ConvertFrom-Json) | select display_name, id + } else { + Write-Error "Failed to create new NSX-T Service" + } + } +} \ No newline at end of file diff --git a/Modules/VMware.VMC/VMware.VMC.psd1 b/Modules/VMware.VMC/VMware.VMC.psd1 new file mode 100755 index 0000000..105fec0 Binary files /dev/null and b/Modules/VMware.VMC/VMware.VMC.psd1 differ diff --git a/Modules/VMware.VMC/VMware.VMC.psm1 b/Modules/VMware.VMC/VMware.VMC.psm1 new file mode 100644 index 0000000..de3d1ab --- /dev/null +++ b/Modules/VMware.VMC/VMware.VMC.psm1 @@ -0,0 +1,1308 @@ +Function Get-VMCCommand { +<# + .NOTES + =========================================================================== + Created by: VMware + Date: 11/17/2017 + Organization: VMware + Blog: http://vmware.com/go/powercli + Twitter: @powercli + =========================================================================== + + .SYNOPSIS + Returns all cmdlets for VMware Cloud on AWS + .DESCRIPTION + This cmdlet will allow you to return all cmdlets included in the VMC module + .EXAMPLE + Get-VMCCommand + .EXAMPLE + Get-Command -Module VMware.VMC + .NOTES + You can either use this cmdlet or the Get-Command cmdlet as seen in Example 2 +#> + Get-command -Module VMware.VimAutomation.Vmc + Get-Command -Module VMware.VMC + +} +Function Connect-VMCVIServer { +<# + .NOTES + =========================================================================== + Created by: VMware + Date: 11/17/2017 + Organization: VMware + Blog: http://vmware.com/go/powercli + Twitter: @powercli + =========================================================================== + + .SYNOPSIS + Cmdlet to connect to your VMC vCenter Server + .DESCRIPTION + This will connect you to both the VMC ViServer as well as the CiSServer at the same time. + .EXAMPLE + Connect-VMCVIServer -Server -User -Password + .NOTES + Easiest way is to pipe through your credentials from Get-VMCSDDCDefaultCredential +#> + Param ( + [Parameter(Mandatory=$true)]$Org, + [Parameter(Mandatory=$true)]$Sddc, + [switch]$Autologin + ) + + If (-Not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect" } Else { + $creds = Get-VMCSDDCDefaultCredential -Org $Org -Sddc $Sddc + Write-Host "Connecting to VMC vCenter Server" $creds.vc_public_ip + Connect-VIServer -Server $creds.vc_public_ip -User $creds.cloud_username -Password $creds.cloud_password | Add-Member -MemberType Noteproperty -Name Location -Value "VMC" + Write-Host "Connecting to VMC CIS Endpoint" $creds.vc_public_ip + Connect-CisServer -Server $creds.vc_public_ip -User $creds.cloud_username -Password $creds.cloud_password | Add-Member -MemberType Noteproperty -Name Location -Value "VMC" + } +} +Function Get-VMCOrg { +<# + .NOTES + =========================================================================== + Created by: VMware + Date: 11/17/2017 + Organization: VMware + Blog: http://vmware.com/go/powercli + Twitter: @powercli + =========================================================================== + + .SYNOPSIS + Return the Orgs that you are a part of + .DESCRIPTION + Depending on what you've purchased, you may be a part of one or more VMC Orgs. This will return your orgs + .EXAMPLE + Get-VMCOrg + .EXAMPLE + Get-VMCOrg -Name + .NOTES + Return all the info about the orgs you are a part of +#> + Param ( + [Parameter(Mandatory=$false)]$Name + ) + + If (-Not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use Connect-VMC to connect" } Else { + $orgService = Get-VMCService com.vmware.vmc.orgs + if ($PSBoundParameters.ContainsKey("Name")){ + $orgs = $orgService.list() | Where {$_.display_name -match $Name} + } Else { + $orgs = $orgService.list() + } + $Orgs | Select display_name, name, user_name, created, id + } +} +Function Get-VMCSDDC { +<# + .NOTES + =========================================================================== + Created by: VMware + Date: 11/17/2017 + Organization: VMware + Blog: http://vmware.com/go/powercli + Twitter: @powercli + =========================================================================== + + .SYNOPSIS + Returns all of the SDDCs you are associated to + .DESCRIPTION + Returns all of the SDDCs ayou are associated to + .EXAMPLE + Get-VMCSDDC -Org + .EXAMPLE + Get-VMCSDDC -Name -Org +#> + Param ( + [Parameter(Mandatory=$True)]$Org, + [Parameter(Mandatory=$false)]$Name + ) + + If (-Not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect" } Else { + if ($PSBoundParameters.ContainsKey("Org")){ + $orgs = Get-VMCOrg -Name $Org + } else { + $orgs = Get-VMCOrg + } + + foreach ($org in $orgs) { + $orgID = $org.ID + $sddcService = Get-VMCService com.vmware.vmc.orgs.sddcs + if ($PSBoundParameters.ContainsKey("Name")){ + $sddcService.list($OrgID) | Where {$_.name -match $Name} + } Else { + $sddcService.list($OrgID) + } + } + } +} +Function Get-VMCTask { +<# + .NOTES + =========================================================================== + Created by: VMware + Date: 11/17/2017 + Organization: VMware + Blog: http://vmware.com/go/powercli + Twitter: @powercli + =========================================================================== + + .SYNOPSIS + Returns all of the VMC Tasks + .DESCRIPTION + Returns all of the VMC Tasks that have either occurred or are in process + .EXAMPLE + Get-VMCTask +#> + Param ( + [Parameter(Mandatory=$True)]$Org + ) + + If (-Not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect" } Else { + if ($PSBoundParameters.ContainsKey("Org")){ + $orgs = Get-VMCOrg -Name $Org + } else { + $orgs = Get-VMCOrg + } + + foreach ($org in $orgs) { + $orgID = $org.ID + $taskService = Get-VMCService com.vmware.vmc.orgs.tasks + $taskService.list($OrgID) | Select * -ExcludeProperty Help + } + } +} +Function Get-VMCSDDCDefaultCredential { +<# + .NOTES + =========================================================================== + Created by: VMware + Date: 11/17/2017 + Organization: VMware + Blog: http://vmware.com/go/powercli + Twitter: @powercli + =========================================================================== + + .SYNOPSIS + Returns the default credential for the SDDC + .DESCRIPTION + Returns the default credential for the sddc + .EXAMPLE + Get-VMCSDDCDefaultCredential -Org + .EXAMPLE + Get-VMCSDDCDefaultCredential -Sddc -Org +#> + Param ( + [Parameter(Mandatory=$true)]$Org, + [Parameter(Mandatory=$false)]$Sddc + ) + + If (-Not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect" } Else { + if ($PSBoundParameters.ContainsKey("Sddc")){ + $sddcs = Get-VMCSDDC -Name $Sddc -Org $Org + } else { + $sddcs = Get-VMCSDDC -Org $Org + } + + foreach ($sddc in $sddcs) { + $sddc.resource_config | Select-object vc_url, vc_management_ip, vc_public_ip, cloud_username, cloud_password + } + } +} +Function Get-VMCSDDCPublicIP { +<# + .NOTES + =========================================================================== + Created by: VMware + Date: 11/17/2017 + Organization: VMware + Blog: http://vmware.com/go/powercli + Twitter: @powercli + =========================================================================== + + .SYNOPSIS + Returns your Public IPs + .DESCRIPTION + Returns your Public IPs + .EXAMPLE + Get-VMCSDDCPublicIP -Org + .EXAMPLE + Get-VMCSDDCPublicIP -Sddc -Org + .NOTES + Return your Public IPs that you have assigned to your account +#> + Param ( + [Parameter(Mandatory=$true)]$Org, + [Parameter(Mandatory=$false)]$Sddc + ) + + If (-Not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect" } Else { + if ($PSBoundParameters.ContainsKey("Sddc")){ + $sddcs = Get-VMCSDDC -Name $Sddc -Org $Org + } else { + $sddcs = Get-VMCSDDC -Org $Org + } + + foreach ($sddc in $sddcs) { + $sddc.resource_config.Public_ip_pool + } + } +} +Function Get-VMCVMHost { + Param ( + [Parameter(Mandatory=$false)]$Sddc, + [Parameter(Mandatory=$true)]$Org + ) + + If (-Not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect" } Else { + if ($PSBoundParameters.ContainsKey("Sddc")){ + $sddcs = Get-VMCSDDC -Name $Sddc -Org $Org + } else { + $sddcs = Get-VMCSDDC -Org $Org + } + + $results = @() + foreach ($sddc in $sddcs) { + foreach ($vmhost in $sddc.resource_config.esx_hosts) { + $tmp = [pscustomobject] @{ + esx_id = $vmhost.esx_id; + name = $vmhost.name; + hostname = $vmhost.hostname; + esx_state = $vmhost.esx_state; + sddc_id = $sddc.id; + org_id = $sddc.org_id; + } + $results += $tmp + } + $results + } + } +} +Function Get-VMCSDDCVersion { +<# + .NOTES + =========================================================================== + Created by: VMware + Date: 11/17/2017 + Organization: VMware + Blog: http://vmware.com/go/powercli + Twitter: @powercli + =========================================================================== + + .SYNOPSIS + Returns SDDC Version + .DESCRIPTION + Returns Version of the SDDC + .EXAMPLE + Get-VMCSDDCVersion -Name -Org +#> + Param ( + [Parameter(Mandatory=$True)]$Org, + [Parameter(Mandatory=$false)]$Name + ) + + If (-Not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect" } Else { + if ($PSBoundParameters.ContainsKey("Org")){ + $orgs = Get-VMCOrg -Name $Org + } else { + $orgs = Get-VMCOrg + } + + foreach ($org in $orgs) { + $orgID = $org.ID + $sddcService = Get-VMCService com.vmware.vmc.orgs.sddcs + if ($PSBoundParameters.ContainsKey("Name")){ + ($sddcService.list($OrgID) | Where {$_.name -match $Name}).resource_config.sddc_manifest | Select *version + } Else { + ($sddcService.list($OrgID)).resource_config.sddc_manifest | Select *version + } + } + } +} +Function Get-VMCFirewallRule { + <# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/19/2017 + Organization: VMware + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Retruns VMC Firewall Rules for a given Gateway (MGW or CGW) + .DESCRIPTION + Retruns VMC Firewall Rules for a given Gateway (MGW or CGW) + .EXAMPLE + Get-VMCFirewallRule -OrgName -SDDCName -GatewayType + .EXAMPLE + Get-VMCFirewallRule -OrgName -SDDCName -GatewayType -ShowAll + #> + param( + [Parameter(Mandatory=$false)][String]$SDDCName, + [Parameter(Mandatory=$false)][String]$OrgName, + [Parameter(Mandatory=$false)][Switch]$ShowAll, + [Parameter(Mandatory=$true)][ValidateSet("MGW","CGW")][String]$GatewayType + ) + + if($GatewayType -eq "MGW") { + $EdgeId = "edge-1" + } else { + $EdgeId = "edge-2" + } + + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + + $firewallConfigService = Get-VmcService com.vmware.vmc.orgs.sddcs.networks.edges.firewall.config + + $firewallRules = ($firewallConfigService.get($orgId, $sddcId, $EdgeId)).firewall_rules.firewall_rules + if(-not $ShowAll) { + $firewallRules = $firewallRules | where { $_.rule_type -ne "default_policy" -and $_.rule_type -ne "internal_high" -and $_.name -ne "vSphere Cluster HA" -and $_.name -ne "Outbound Access" } | Sort-Object -Property rule_tag + } else { + $firewallRules = $firewallRules | Sort-Object -Property rule_tag + } + + $results = @() + foreach ($firewallRule in $firewallRules) { + if($firewallRule.source.ip_address.Count -ne 0) { + $source = $firewallRule.source.ip_address + } else { $source = "ANY" } + + if($firewallRule.application.service.protocol -ne $null) { + $protocol = $firewallRule.application.service.protocol + } else { $protocol = "ANY" } + + if($firewallRule.application.service.port -ne $null) { + $port = $firewallRule.application.service.port + } else { $port = "ANY" } + + $tmp = [pscustomobject] @{ + ID = $firewallRule.rule_id; + Name = $firewallRule.name; + Type = $firewallRule.rule_type; + Action = $firewallRule.action; + Protocol = $protocol; + Port = $port; + SourceAddress = $source + DestinationAddress = $firewallRule.destination.ip_address; + } + $results+=$tmp + } + $results + } +Function Export-VMCFirewallRule { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/19/2017 + Organization: VMware + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Exports all "customer" created VMC Firewall Rules to JSON file + .DESCRIPTION + Exports all "customer" created VMC Firewall Rules to JSON file + .EXAMPLE + Export-VMCFirewallRule -OrgName -SDDCName -GatewayType -Path "C:\Users\lamw\Desktop\VMCFirewallRules.json" + #> + param( + [Parameter(Mandatory=$false)][String]$SDDCName, + [Parameter(Mandatory=$false)][String]$OrgName, + [Parameter(Mandatory=$true)][ValidateSet("MGW","CGW")][String]$GatewayType, + [Parameter(Mandatory=$false)][String]$Path + ) + + if (-not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect"; break } + + if($GatewayType -eq "MGW") { + $EdgeId = "edge-1" + } else { + $EdgeId = "edge-2" + } + + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + + if(-not $orgId) { + Write-Host -ForegroundColor red "Unable to find Org $OrgName, please verify input" + break + } + if(-not $sddcId) { + Write-Host -ForegroundColor red "Unable to find SDDC $SDDCName, please verify input" + break + } + + $firewallConfigService = Get-VmcService com.vmware.vmc.orgs.sddcs.networks.edges.firewall.config + + $firewallRules = ($firewallConfigService.get($orgId, $sddcId, $EdgeId)).firewall_rules.firewall_rules + if(-not $ShowAll) { + $firewallRules = $firewallRules | where { $_.rule_type -ne "default_policy" -and $_.rule_type -ne "internal_high" -and $_.name -ne "vSphere Cluster HA" -and $_.name -ne "Outbound Access" } | Sort-Object -Property rule_tag + } else { + $firewallRules = $firewallRules | Sort-Object -Property rule_tag + } + + $results = @() + $count = 0 + foreach ($firewallRule in $firewallRules) { + if($firewallRule.source.ip_address.Count -ne 0) { + $source = $firewallRule.source.ip_address + } else { + $source = "ANY" + } + + $tmp = [pscustomobject] @{ + Name = $firewallRule.name; + Action = $firewallRule.action; + Protocol = $firewallRule.application.service.protocol; + Port = $firewallRule.application.service.port; + SourcePort = $firewallRule.application.service.source_port; + ICMPType = $firewallRule.application.service.icmp_type; + SourceAddress = $firewallRule.source.ip_address; + DestinationAddress = $firewallRule.destination.ip_address; + Enabled = $firewallRule.enabled; + Logging = $firewallRule.logging_enabled; + } + $count+=1 + $results+=$tmp + } + if($Path) { + Write-Host -ForegroundColor Green "Exporting $count VMC Firewall Rules to $Path ..." + $results | ConvertTo-Json | Out-File $Path + } else { + $results | ConvertTo-Json + } +} +Function Import-VMCFirewallRule { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/19/2017 + Organization: VMware + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Imports VMC Firewall Rules from exported JSON configuration file + .DESCRIPTION + Imports VMC Firewall Rules from exported JSON configuration file + .EXAMPLE + Import-VMCFirewallRule -OrgName -SDDCName -GatewayType -Path "C:\Users\lamw\Desktop\VMCFirewallRules.json" + #> + param( + [Parameter(Mandatory=$false)][String]$SDDCName, + [Parameter(Mandatory=$false)][String]$OrgName, + [Parameter(Mandatory=$true)][ValidateSet("MGW","CGW")][String]$GatewayType, + [Parameter(Mandatory=$false)][String]$Path + ) + + if (-not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect"; break } + + if($GatewayType -eq "MGW") { + $EdgeId = "edge-1" + } else { + $EdgeId = "edge-2" + } + + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + + if(-not $orgId) { + Write-Host -ForegroundColor red "Unable to find Org $OrgName, please verify input" + break + } + if(-not $sddcId) { + Write-Host -ForegroundColor red "Unable to find SDDC $SDDCName, please verify input" + break + } + + $firewallService = Get-VmcService com.vmware.vmc.orgs.sddcs.networks.edges.firewall.config.rules + + $vmcFirewallRulesJSON = Get-Content -Raw $Path | ConvertFrom-Json + + # Create top level Firewall Rules Object + $firewallRules = $firewallService.Help.add.firewall_rules.Create() + # Create top top level Firewall Rule Spec which will be an array of individual Firewall rules as we process them in next section + $ruleSpec = $firewallService.Help.add.firewall_rules.firewall_rules.Create() + + foreach ($vmcFirewallRule in $vmcFirewallRulesJSON) { + # Create Individual Firewall Rule Element Spec + $ruleElementSpec = $firewallService.Help.add.firewall_rules.firewall_rules.Element.Create() + + # AppSpec + $appSpec = $firewallService.Help.add.firewall_rules.firewall_rules.Element.application.Create() + # ServiceSpec + $serviceSpec = $firewallService.Help.add.firewall_rules.firewall_rules.Element.application.service.Element.Create() + + $protocol = $null + if($vmcFirewallRule.Protocol -ne $null) { + $protocol = $vmcFirewallRule.Protocol + } + $serviceSpec.protocol = $protocol + + # Process ICMP Type from JSON + $icmpType = $null + if($vmcFirewallRule.ICMPType -ne $null) { + $icmpType = $vmcFirewallRule.ICMPType + } + $serviceSpec.icmp_type = $icmpType + + # Process Source Ports from JSON + $sourcePorts = @() + if($vmcFirewallRule.SourcePort -eq "any" -or $vmcFirewallRule.SourcePort -ne $null) { + foreach ($port in $vmcFirewallRule.SourcePort) { + $sourcePorts+=$port + } + } else { + $sourcePorts = @("any") + } + $serviceSpec.source_port = $sourcePorts + + # Process Ports from JSON + $ports = @() + if($vmcFirewallRule.Port -ne "null") { + foreach ($port in $vmcFirewallRule.Port) { + $ports+=$port + } + } + $serviceSpec.port = $ports + $addSpec = $appSpec.service.Add($serviceSpec) + + # Create Source Spec + $srcSpec = $firewallService.Help.add.firewall_rules.firewall_rules.Element.source.Create() + $srcSpec.exclude = $false + # Process Source Address from JSON + $sourceAddess = @() + if($vmcFirewallRule.SourceAddress -ne "null") { + foreach ($address in $vmcFirewallRule.SourceAddress) { + $sourceAddess+=$address + } + } + $srcSpec.ip_address = $sourceAddess; + + # Create Destination Spec + $destSpec = $firewallService.Help.add.firewall_rules.firewall_rules.Element.destination.Create() + $destSpec.exclude = $false + # Process Destination Address from JSON + $destinationAddess = @() + if($vmcFirewallRule.DestinationAddress -ne "null") { + foreach ($address in $vmcFirewallRule.DestinationAddress) { + $destinationAddess+=$address + } + } + $destSpec.ip_address = $destinationAddess + + # Add various specs + if($vmcFirewallRule.Protocol -ne $null -and $vmcFirewallRule.port -ne $null) { + $ruleElementSpec.application = $appSpec + } + + $ruleElementSpec.source = $srcSpec + $ruleElementSpec.destination = $destSpec + $ruleElementSpec.rule_type = "user" + + # Process Enabled from JSON + $fwEnabled = $false + if($vmcFirewallRule.Enabled -eq "true") { + $fwEnabled = $true + } + $ruleElementSpec.enabled = $fwEnabled + + # Process Logging from JSON + $loggingEnabled = $false + if($vmcFirewallRule.Logging -eq "true") { + $loggingEnabled = $true + } + $ruleElementSpec.logging_enabled = $loggingEnabled + + $ruleElementSpec.action = $vmcFirewallRule.Action + $ruleElementSpec.name = $vmcFirewallRule.Name + + # Add the individual FW rule spec into our overall firewall rules array + Write-host "Creating VMC Firewall Rule Spec:" $vmcFirewallRule.Name "..." + $ruleSpecAdd = $ruleSpec.Add($ruleElementSpec) + } + $firewallRules.firewall_rules = $ruleSpec + + Write-host "Adding VMC Firewall Rules ..." + $firewallRuleAdd = $firewallService.add($orgId,$sddcId,$EdgeId,$firewallRules) +} +Function Remove-VMCFirewallRule { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/19/2017 + Organization: VMware + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Removes VMC Firewall Rule given Rule Id + .DESCRIPTION + Removes VMC Firewall Rule given Rule Id + .EXAMPLE + Remove-VMCFirewallRule -OrgName -SDDCName -GatewayType -RuleId + #> + param( + [Parameter(Mandatory=$false)][String]$SDDCName, + [Parameter(Mandatory=$false)][String]$OrgName, + [Parameter(Mandatory=$true)][ValidateSet("MGW","CGW")][String]$GatewayType, + [Parameter(Mandatory=$false)][String]$RuleId + ) + + if (-not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect"; break } + + if($GatewayType -eq "MGW") { + $EdgeId = "edge-1" + } else { + $EdgeId = "edge-2" + } + + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + + if(-not $orgId) { + Write-Host -ForegroundColor red "Unable to find Org $OrgName, please verify input" + break + } + if(-not $sddcId) { + Write-Host -ForegroundColor red "Unable to find SDDC $SDDCName, please verify input" + break + } + + $firewallService = Get-VmcService com.vmware.vmc.orgs.sddcs.networks.edges.firewall.config.rules + Write-Host "Removing VMC Firewall Rule Id $RuleId ..." + $firewallService.delete($orgId,$sddcId,$EdgeId,$RuleId) +} +Function Get-VMCLogicalNetwork { + <# + .NOTES + =========================================================================== + Created by: Kyle Ruddy + Date: 03/06/2018 + Organization: VMware + Blog: https://thatcouldbeaproblem.com + Twitter: @kmruddy + =========================================================================== + + .SYNOPSIS + Retruns VMC Logical Networks for a given SDDC + .DESCRIPTION + Retruns VMC Logical Networks for a given SDDC + .EXAMPLE + Get-VMCLogicalNetwork -OrgName -SDDCName + .EXAMPLE + Get-VMCLogicalNetwork -OrgName -SDDCName -LogicalNetworkName + #> + param( + [Parameter(Mandatory=$true)][String]$SDDCName, + [Parameter(Mandatory=$true)][String]$OrgName, + [Parameter(Mandatory=$false)][String]$LogicalNetworkName + + ) + + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + + if(-not $orgId) { + Write-Host -ForegroundColor red "Unable to find Org $OrgName, please verify input" + break + } + if(-not $sddcId) { + Write-Host -ForegroundColor red "Unable to find SDDC $SDDCName, please verify input" + break + } + + # @LucD22 - 21/10/18 - Fix for issue #176 VMware.VMC module only lists firts 20 Logical networks + # Loop until entries (total_count) are returned + + $index = [long]0 + + $logicalNetworks = do{ + $netData = $logicalNetworkService.get_0($orgId,$sddcId,$pagesize,$index) + $netData.data | Sort-Object -Property id + $index = $index + $netdata.paging_info.page_size + } + until($index -ge $netData.paging_info.total_count) + + if($LogicalNetworkName) { + $logicalNetworks = $logicalNetworks | Where-Object {$_.Name -eq $LogicalNetworkName} + } + + $results = @() + foreach ($logicalNetwork in $logicalNetworks) { + $tmp = [pscustomobject] @{ + ID = $logicalNetwork.id; + Name = $logicalNetwork.name; + SubnetMask = $logicalNetwork.subnets.address_groups.prefix_length; + Gateway = $logicalNetwork.subnets.address_groups.primary_address; + DHCPipRange = $logicalNetwork.dhcp_configs.ip_pools.ip_range; + DHCPdomain = $logicalNetwork.dhcp_configs.ip_pools.domain_name; + CGatewayID = $logicalNetwork.cgw_id; + CGateway = $logicalNetwork.cgw_name; + } + $results+=$tmp + } + $results +} +Function Remove-VMCLogicalNetwork { + <# + .NOTES + =========================================================================== + Created by: Kyle Ruddy + Date: 03/06/2018 + Organization: VMware + Blog: https://thatcouldbeaproblem.com + Twitter: @kmruddy + =========================================================================== + + .SYNOPSIS + Removes Logical Network given ID + .DESCRIPTION + Removes Logical Network given ID + .EXAMPLE + Remove-VMCLogicalNetwork -OrgName -SDDCName -LogicalNetworkName + #> + [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact='High')] + param( + [Parameter(Mandatory=$true)][String]$SDDCName, + [Parameter(Mandatory=$true)][String]$OrgName, + [Parameter(Mandatory=$true)][String]$LogicalNetworkName + ) + + if (-not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect"; break } + + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + $lsId = (Get-VMCLogicalNetwork -OrgName $OrgName -SDDCName $SDDCName -LogicalNetworkName $LogicalNetworkName).Id + + if(-not $orgId) { + Write-Host -ForegroundColor red "Unable to find Org $OrgName, please verify input" + break + } + if(-not $sddcId) { + Write-Host -ForegroundColor red "Unable to find SDDC $SDDCName, please verify input" + break + } + if(-not $lsId) { + Write-Host -ForegroundColor red "Unable to find SDDC $LogicalNetworkName, please verify input" + break + } + + $logicalNetworkService = Get-VmcService com.vmware.vmc.orgs.sddcs.networks.logical + $logicalNetworkService.delete($orgId,$sddcId,$lsId) +} +Function New-VMCLogicalNetwork { +<# + .NOTES + =========================================================================== + Created by: Kyle Ruddy + Date: 03/06/2018 + Organization: VMware + Blog: https://thatcouldbeaproblem.com + Twitter: @kmruddy + =========================================================================== + + .SYNOPSIS + Creates a new Logical Network + .DESCRIPTION + Creates a new Logical Network + .EXAMPLE + New-VMCLogicalNetwork -OrgName -SDDCName -LogicalNetworkName -SubnetMask -Gateway +#> + [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact='High')] + param( + [Parameter(Mandatory=$true)][String]$SDDCName, + [Parameter(Mandatory=$true)][String]$OrgName, + [Parameter(Mandatory=$true)][String]$LogicalNetworkName, + [Parameter(Mandatory=$true)][String]$SubnetMask, + [Parameter(Mandatory=$true)][String]$Gateway + ) + + if (-not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect"; break } + + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + + if(-not $orgId) { + Write-Host -ForegroundColor red "Unable to find Org $OrgName, please verify input" + break + } + if(-not $sddcId) { + Write-Host -ForegroundColor red "Unable to find SDDC $SDDCName, please verify input" + break + } + + $logicalNetworkService = Get-VmcService com.vmware.vmc.orgs.sddcs.networks.logical + $logicalNetworkSpec = $logicalNetworkService.Help.create.sddc_network.Create() + $logicalNetworkSpec.name = $LogicalNetworkName + $logicalNetworkSpec.cgw_id = "edge-2" + $logicalNetworkSpec.cgw_name = "SDDC-CGW-1" + $logicalNetworkAddressGroupSpec = $logicalNetworkService.Help.create.sddc_network.subnets.address_groups.Element.Create() + $logicalNetworkAddressGroupSpec.prefix_length = $SubnetMask + $logicalNetworkAddressGroupSpec.primary_address = $Gateway + + $logicalNetworkSpec.subnets.address_groups.Add($logicalNetworkAddressGroupSpec) | Out-Null + $logicalNetworkService.create($orgId, $sddcId, $logicalNetworkSpec) + Get-VMCLogicalNetwork -OrgName $OrgName -SDDCName $SDDCName -LogicalNetworkName $LogicalNetworkName +} +Function Get-VMCSDDCSummary { + <# + .NOTES + =========================================================================== + Created by: VMware + Date: 09/04/18 + Organization: VMware + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Returns a number of useful informational data about a given SDDC within VMC Org + .DESCRIPTION + Returns Version, Create/Expiration Date, Deployment Type, Region, AZ, Instance Type, VPC CIDR & NSX-T + .EXAMPLE + Get-VMCSDDCSummary -Name -Org + #> + Param ( + [Parameter(Mandatory=$True)]$OrgName, + [Parameter(Mandatory=$True)]$SDDCName + ) + + If (-Not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect" } Else { + $orgId = (Get-VMCOrg -Name $Org).Id + $sddcId = (Get-VMCSDDC -Name $Name -Org $Org).Id + + $sddcService = Get-VmcService "com.vmware.vmc.orgs.sddcs" + $sddc = $sddcService.get($orgId,$sddcId) + + $results = [pscustomobject] @{ + Version = $sddc.resource_config.sddc_manifest.vmc_version; + CreateDate = $sddc.created; + ExpirationDate = $sddc.expiration_date; + DeploymentType = $sddc.resource_config.deployment_type; + Region = $sddc.resource_config.region; + AvailabilityZone = $sddc.resource_config.availability_zones; + InstanceType = $sddc.resource_config.sddc_manifest.esx_ami.instance_type; + VpcCIDR = $sddc.resource_config.vpc_info.vpc_cidr; + NSXT = $sddc.resource_config.nsxt; + } + $results + } +} +Function Get-VMCPublicIP { + <# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/12/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Retrieves all public IP Addresses for a given SDDC + .DESCRIPTION + This cmdlet retrieves all public IP Address for a given SDDC + .EXAMPLE + Get-VMCPublicIP -OrgName $OrgName -SDDCName $SDDCName + #> + Param ( + [Parameter(Mandatory=$True)]$OrgName, + [Parameter(Mandatory=$True)]$SDDCName + ) + + If (-Not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect" } Else { + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + + $publicIPService = Get-VmcService "com.vmware.vmc.orgs.sddcs.publicips" + $publicIPs = $publicIPService.list($orgId,$sddcId) + + $publicIPs | select public_ip, name, allocation_id + } + } +Function New-VMCPublicIP { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/12/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Request a new public IP Address for a given SDDC + .DESCRIPTION + This cmdlet requests a new public IP Address for a given SDDC + .EXAMPLE + New-VMCPublicIP -OrgName $OrgName -SDDCName $SDDCName -Description "Test for Randy" +#> + Param ( + [Parameter(Mandatory=$True)]$OrgName, + [Parameter(Mandatory=$True)]$SDDCName, + [Parameter(Mandatory=$False)]$Description + ) + + If (-Not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect" } Else { + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + + $publicIPService = Get-VmcService "com.vmware.vmc.orgs.sddcs.publicips" + + $publicIPSpec = $publicIPService.Help.create.spec.Create() + $publicIPSpec.count = 1 + $publicIPSpec.names = @($Description) + + Write-Host "Requesting a new public IP Address for your SDDC ..." + $results = $publicIPService.create($orgId,$sddcId,$publicIPSpec) + } +} +Function Remove-VMCPublicIP { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 09/12/2018 + Organization: VMware + Blog: http://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Removes a specific public IP Addresses for a given SDDC + .DESCRIPTION + This cmdlet removes a specific public IP Address for a given SDDC + .EXAMPLE + Remove-VMCPublicIP -OrgName $OrgName -SDDCName $SDDCName -AllocationId "eipalloc-0567acf34e436c01f" +#> + Param ( + [Parameter(Mandatory=$True)]$OrgName, + [Parameter(Mandatory=$True)]$SDDCName, + [Parameter(Mandatory=$True)]$AllocationId + ) + + If (-Not $global:DefaultVMCServers) { Write-error "No VMC Connection found, please use the Connect-VMC to connect" } Else { + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + + $publicIPService = Get-VmcService "com.vmware.vmc.orgs.sddcs.publicips" + + Write-Host "Deleting public IP Address with ID $AllocationId ..." + $results = $publicIPService.delete($orgId,$sddcId,$AllocationId) + } +} +Function Get-VMCEdge { +<# +.NOTES +=========================================================================== +Created by: Luc Dekens +Date: 23/10/2018 +Organization: Community +Blog: http://lucd.info +Twitter: @LucD22 +=========================================================================== + +.SYNOPSIS + Returns all the VMC Edges +.DESCRIPTION + Returns all the VMC Edges +.EXAMPLE + Get-VMCEdge -OrgName $orgName -SddcName $SDDCName -EdgeType gatewayServices +#> + Param ( + [Parameter(Mandatory=$True)] + [string]$OrgName, + [Parameter(Mandatory=$True)] + [string]$SDDCName, + [ValidateSet('gatewayServices','distributedRouter')] + [string]$EdgeType = '' + ) + + If (-Not $global:DefaultVMCServers) { + Write-error "No VMC Connection found, please use the Connect-VMC to connect" + } + Else { + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + + $edgeService = Get-VmcService -Name 'com.vmware.vmc.orgs.sddcs.networks.edges' + $index = [long]0 + $edges = do{ + $edgeData = $edgeService.get($orgId,$sddcId,$EdgeType,'',$index) + $edgeData.edge_page.data | Sort-Object -Property id + $index = $index + $edgeData.edge_page.paging_info.page_size + } + until($index -ge $edgeData.paging_info.total_count) + $edges | %{ + [pscustomobject]@{ + Name = $_.Name + Id = $_.id + Type = $_.edge_type + State = $_.state + Status = $_.edge_status + VNics = $_.number_of_connected_vnics + TenantId = $_.tenant_id + } + } + } +} +Function Get-VMCEdgeStatus { +<# +.NOTES +=========================================================================== +Created by: Luc Dekens +Date: 23/10/2018 +Organization: Community +Blog: http://lucd.info +Twitter: @LucD22 +=========================================================================== + +.SYNOPSIS + Returns the status of the gateway +.DESCRIPTION + Retrieve the status of the specified management or compute gateway (NSX Edge). +.EXAMPLE + Get-VMCEdgeStatus -OrgName $orgName -SddcName $SDDCName -Edge $EdgeName +#> + Param ( + [Parameter(Mandatory=$True)] + [string]$OrgName, + [Parameter(Mandatory=$True)] + [string]$SDDCName, + [Parameter(Mandatory=$True)] + [string]$EdgeName + ) + + If (-Not $global:DefaultVMCServers) { + Write-error "No VMC Connection found, please use the Connect-VMC to connect" + } + Else { + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + $edgeId = Get-VMCEdge -SDDCName $SDDCName -Org $OrgName | where{$_.Name -eq $EdgeName} | select -ExpandProperty Id + + $statusService = Get-VmcService -Name 'com.vmware.vmc.orgs.sddcs.networks.edges.status' + $status = $statusService.get($orgId,$sddcId,$edgeId) + + $vmStatus = $status.edge_vm_status | %{ + [pscustomobject]@{ + Name = $_.name + State = $_.edge_VM_status + HAState = $_.ha_state + Index = $_.index + } + } + $featureStatus = $status.feature_statuses | %{ + [pscustomobject]@{ + Service = $_.service + Status = $_.status + } + } + [pscustomobject]@{ + Time = [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($status.timestamp/1000)) + Status = $status.edge_status + PublishStatus = $status.publish_status + SystemStatus = $_.system_status + NicInUse = $status.ha_vnic_in_use + } + } +} +Function Get-VMCEdgeNic { +<# +.NOTES +=========================================================================== +Created by: Luc Dekens +Date: 23/10/2018 +Organization: Community +Blog: http://lucd.info +Twitter: @LucD22 +=========================================================================== + +.SYNOPSIS + Returns all interfaces for the gateway +.DESCRIPTION + Retrieve all interfaces for the specified management or compute gateway (NSX Edge). +.EXAMPLE + Get-VMCEdgeNic -OrgName $orgName -SddcName $SDDCName -Edge $EdgeName +#> + Param ( + [Parameter(Mandatory=$True)] + [string]$OrgName, + [Parameter(Mandatory=$True)] + [string]$SDDCName, + [Parameter(Mandatory=$True)] + [string]$EdgeName + ) + + If (-Not $global:DefaultVMCServers) { + Write-error "No VMC Connection found, please use the Connect-VMC to connect" + } + Else { + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + $edgeId = Get-VMCEdge -SDDCName $SDDCName -Org $OrgName | where{$_.Name -eq $EdgeName} | select -ExpandProperty Id + + $vnicService = Get-VmcService -Name 'com.vmware.vmc.orgs.sddcs.networks.edges.vnics' + $vnicService.get($orgId,$sddcId,$edgeId) | select -ExpandProperty vnics | %{ + [pscustomobject]@{ + Label = $_.label + Name = $_.Name + Type = $_.type + Index = $_.index + IsConnected = $_.is_connected + Portgroup = $_.portgroup_name + } + } + } +} +Function Get-VMCEdgeNicStat { +<# +.NOTES +=========================================================================== +Created by: Luc Dekens +Date: 23/10/2018 +Organization: Community +Blog: http://lucd.info +Twitter: @LucD22 +=========================================================================== + +.SYNOPSIS + Returns statistics for the gateway interfaces +.DESCRIPTION + Retrieve interface statistics for a management or compute gateway (NSX Edge). +.EXAMPLE + Get-VMCEdgeNicStat -OrgName $orgName -SddcName $SDDCName -Edge $EdgeName +#> + [CmdletBinding(DefaultParameterSetName='Default')] + Param ( + [Parameter(Mandatory=$True)] + [string]$OrgName, + [Parameter(Mandatory=$True)] + [string]$SDDCName, + [Parameter(Mandatory=$True)] + [string]$EdgeName +# [DateTime]$Start, +# [DateTime]$Finish + ) + + If (-Not $global:DefaultVMCServers) { + Write-error "No VMC Connection found, please use the Connect-VMC to connect" + } + Else { + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + $edgeId = Get-VMCEdge -SDDCName $SDDCName -Org $OrgName | where{$_.Name -eq $EdgeName} | select -ExpandProperty Id + +# $epoch = Get-Date 01/01/1970 +# +# if($start){ +# $startEpoch = (New-TimeSpan -Start $epoch -End $Start.ToUniversalTime()).TotalMilliseconds +# } +# if($Finish){ +# $finishEpoch = (New-TimeSpan -Start $epoch -End $Finish.ToUniversalTime()).TotalMilliseconds +# } + + $vnicStatService = Get-VmcService -Name 'com.vmware.vmc.orgs.sddcs.networks.edges.statistics.interfaces' +# $stats = $vnicStatService.get($orgId,$sddcId,$edgeId,[long]$startEpoch,[long]$finishEpoch) + $stats = $vnicStatService.get($orgId,$sddcId,$edgeId) + + $stats.data_dto | Get-Member -MemberType NoteProperty | where{$_.Name -ne 'Help'} | %{$_.Name} | %{ + $stats.data_dto."$_" | %{ + [pscustomobject]@{ + vNIC = $_.vnic + Timestamp = [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($_.timestamp)) + In = $_.in + Out = $_.out + Unit = 'Kbps' + Interval = $stats.meta_dto.interval + } + } + } + } +} +Function Get-VMCEdgeUplinkStat { +<# +.NOTES +=========================================================================== +Created by: Luc Dekens +Date: 23/10/2018 +Organization: Community +Blog: http://lucd.info +Twitter: @LucD22 +=========================================================================== + +.SYNOPSIS + Returns statistics for the uplink interfaces +.DESCRIPTION + Retrieve uplink interface statistics for a management or compute gateway (NSX Edge). +.EXAMPLE + Get-VMCEdgeUplinkStat -OrgName $orgName -SddcName $SDDCName -Edge $EdgeName +#> + Param ( + [Parameter(Mandatory=$True)] + [string]$OrgName, + [Parameter(Mandatory=$True)] + [string]$SDDCName, + [Parameter(Mandatory=$True)] + [string]$EdgeName +# [DateTime]$Start, +# [DateTime]$Finish + ) + + If (-Not $global:DefaultVMCServers) { + Write-error "No VMC Connection found, please use the Connect-VMC to connect" + } + Else { + $orgId = (Get-VMCOrg -Name $OrgName).Id + $sddcId = (Get-VMCSDDC -Name $SDDCName -Org $OrgName).Id + $edgeId = Get-VMCEdge -SDDCName $SDDCName -Org $OrgName | where{$_.Name -eq $EdgeName} | select -ExpandProperty Id + +# $epoch = Get-Date 01/01/1970 +# +# if($start){ +# $startEpoch = (New-TimeSpan -Start $epoch -End $Start.ToUniversalTime()).TotalMilliseconds +# } +# if($Finish){ +# $finishEpoch = (New-TimeSpan -Start $epoch -End $Finish.ToUniversalTime()).TotalMilliseconds +# } + + $uplinkStatService = Get-VmcService -Name 'com.vmware.vmc.orgs.sddcs.networks.edges.statistics.interfaces.uplink' +# $stats = $uplinkStatService.get($orgId,$sddcId,$edgeId,[long]$startEpoch,[long]$finishEpoch) + $stats = $uplinkStatService.get($orgId,$sddcId,$edgeId) + + $stats.data_dto | Get-Member -MemberType NoteProperty | where{$_.Name -ne 'Help'} | %{$_.Name} | %{ + if($stats.data_dto."$_".Count -ne 0){ + $stats.data_dto."$_" | %{ + [pscustomobject]@{ + vNIC = $_.vnic + Timestamp = [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($_.timestamp)) + In = $_.in + Out = $_.out + Unit = 'Kbps' + Interval = $stats.meta_dto.interval + } + } + } + } + } +} + +Export-ModuleMember -Function 'Get-VMCCommand', 'Connect-VMCVIServer', 'Get-VMCOrg', 'Get-VMCSDDC', + 'Get-VMCTask', 'Get-VMCSDDCDefaultCredential', 'Get-VMCSDDCPublicIP', 'Get-VMCVMHost', + 'Get-VMCSDDCVersion', 'Get-VMCFirewallRule', 'Export-VMCFirewallRule', 'Import-VMCFirewallRule', + 'Remove-VMCFirewallRule', 'Get-VMCLogicalNetwork', 'Remove-VMCLogicalNetwork', 'New-VMCLogicalNetwork', + 'Get-VMCSDDCSummary', 'Get-VMCPublicIP', 'New-VMCPublicIP', 'Remove-VMCPublicIP', + 'Get-VMCEdge', 'Get-VMCEdgeNic', 'Get-VMCEdgeStatus', 'Get-VMCEdgeNicStat', 'Get-VMCEdgeUplinkStat' diff --git a/Modules/VMware.VMEncryption/README.md b/Modules/VMware.VMEncryption/README.md index 9e38900..d01c5fa 100644 --- a/Modules/VMware.VMEncryption/README.md +++ b/Modules/VMware.VMEncryption/README.md @@ -2,6 +2,31 @@ Prerequisites/Steps to use this module: 1. This module only works for vSphere products that support VM Encryption. E.g. vSphere 6.5 and later. 2. All the functions in this module only work for KMIP Servers. -3. Install the latest version of Powershell and PowerCLI(6.5). +3. Install the latest version of Powershell and PowerCLI. 4. Import this module by running: Import-Module -Name "location of this module" -5. Get-Command -Module "This module Name" to list all available functions. \ No newline at end of file +5. Get-Command -Module "This module Name" to list all available functions. + +Note: +Deprecating the below functions related to KMServer and KMSCluster from VMware.VMEncryption and using instead the ones from VMware.VimAutomation.Storage, + +1, VMware.VMEncryption\Get-DefaultKMSCluster, use instead +VMware.VimAutomation.Storage\Get-KmsCluster|where {$_.UseAsDefaultKeyProvider}|foreach {$_.id} + +2, VMware.VMEncryption\Get-KMSCluster, use instead +VMware.VimAutomation.Storage\Get-KmsCluster|select id + +3, VMware.VMEncryption\Get-KMSClusterInfo, use instead +VMware.VimAutomation.Storage\Get-KmsCluster|foreach {$_.extensiondata} + +4, VMware.VMEncryption\Get-KMServerInfo, use instead +VMware.VimAutomation.Storage\Get-KeyManagementServer|foreach {$_.extensiondata} + +5, VMware.VMEncryption\New-KMServer, use instead +VMware.VimAutomation.Storage\Add-KeyManagementServer + +6, VMware.VMEncryption\Remove-KMServer, use instead +VMware.VimAutomation.Storage\Remove-KeyManagementServer + +7, VMware.VMEncryption\Set-DefaultKMSCluster, use instead +VMware.VimAutomation.Storage\Set-KmsCluster -UseAsDefaultKeyProvider + diff --git a/Modules/VMware.VMEncryption/VMware.VMEncryption.psd1 b/Modules/VMware.VMEncryption/VMware.VMEncryption.psd1 index d310632..8a93a97 100644 Binary files a/Modules/VMware.VMEncryption/VMware.VMEncryption.psd1 and b/Modules/VMware.VMEncryption/VMware.VMEncryption.psd1 differ diff --git a/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 index c350955..df37e98 100644 --- a/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 +++ b/Modules/VMware.VMEncryption/VMware.VMEncryption.psm1 @@ -1,5 +1,5 @@ # Script Module : VMware.VMEncryption -# Version : 1.0 +# Version : 1.2 # Copyright © 2016 VMware, Inc. All Rights Reserved. @@ -56,8 +56,13 @@ New-VIProperty -Name EncryptionKeyId -ObjectType VirtualMachine -Value { New-VIProperty -Name Locked -ObjectType VirtualMachine -Value { Param ($VM) - ($vm.extensiondata.Runtime.ConnectionState -eq "invalid") -and ($vm.extensiondata.Config.KeyId) -} -BasedOnExtensionProperty 'Runtime.ConnectionState','Config.KeyId' -Force | Out-Null + if ($vm.ExtensionData.Runtime.CryptoState) { + $vm.ExtensionData.Runtime.CryptoState -eq "locked" + } + else { + ($vm.extensiondata.Runtime.ConnectionState -eq "invalid") -and ($vm.extensiondata.Config.KeyId) + } +} -BasedOnExtensionProperty 'Runtime.CryptoState', 'Runtime.ConnectionState','Config.KeyId' -Force | Out-Null New-VIProperty -Name vMotionEncryption -ObjectType VirtualMachine -Value { Param ($VM) @@ -83,6 +88,13 @@ New-VIProperty -Name EncryptionKeyId -ObjectType HardDisk -Value { } } -BasedOnExtensionProperty 'Backing.KeyId' -Force | Out-Null +New-VIProperty -Name KMSserver -ObjectType VMHost -Value { + Param ($VMHost) + if ($VMHost.CryptoSafe) { + $VMHost.ExtensionData.Runtime.CryptoKeyId.ProviderId.Id + } +} -BasedOnExtensionProperty 'Runtime.CryptoKeyId.ProviderId.Id' -Force | Out-Null + Function Enable-VMHostCryptoSafe { <# .SYNOPSIS @@ -106,13 +118,6 @@ Function Enable-VMHostCryptoSafe { .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -174,13 +179,6 @@ Function Set-VMHostCryptoKey { .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -259,13 +257,6 @@ Function Set-vMotionEncryptionConfig { .NOTES Author : Brian Graf, Carrie Yang. Author email : grafb@vmware.com, yangm@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -341,13 +332,6 @@ Function Enable-VMEncryption { .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -501,13 +485,6 @@ Function Enable-VMDiskEncryption { .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -653,13 +630,6 @@ Function Disable-VMEncryption { .NOTES Author : Carrie Yang. Author email : yangm@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -749,13 +719,6 @@ Function Disable-VMDiskEncryption { .NOTES Author : Carrie Yang. Author email : yangm@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -875,7 +838,7 @@ Function Set-VMEncryptionKey { C:\PS>$VM|Set-VMEncryptionKey -KMSClusterId $KMSCluster.Id -Deep Deep rekeys the VM Home and all its disks using a new key. - The key is generted from the KMS whose clusterId is $KMSCluster.Id. + The key is generated from the KMS whose clusterId is $KMSCluster.Id. .NOTES This cmdlet assumes there is already a KMS in vCenter Server. If VM is not encrypted, the cmdlet quits. @@ -884,13 +847,6 @@ Function Set-VMEncryptionKey { .NOTES Author : Carrie Yang. Author email : yangm@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -1027,10 +983,10 @@ Function Set-VMDiskEncryptionKey { C:\PS>$KMSCluster = Get-KMSCluster | select -last 1 C:\PS>$VM = Get-VM -Name win2012 C:\PS>$HardDisk = get-vm $vm|Get-HardDisk - C:\PS>$HardDisk|$Set-VMEncryptionKey -VM $VM -KMSClusterId $KMSCluster.Id -Deep + C:\PS>$HardDisk| Set-VMDiskEncryptionKey -VM $VM -KMSClusterId $KMSCluster.Id -Deep Deep rekeys all the disks of the $VM using a new key. - The key is generted from the KMS whose clusterId is $KMSCluster.Id. + The key is generated from the KMS whose clusterId is $KMSCluster.Id. .NOTES This cmdlet assumes there is already a KMS in vCenter Server. @@ -1040,13 +996,6 @@ Function Set-VMDiskEncryptionKey { .NOTES Author : Carrie Yang. Author email : yangm@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -1163,13 +1112,6 @@ Function Get-VMEncryptionInfo { .NOTES Author : Carrie Yang. Author email : yangm@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -1262,13 +1204,6 @@ Function Get-EntityByCryptoKey { .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -1387,13 +1322,6 @@ Function New-KMServer { .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -1428,6 +1356,7 @@ Function New-KMServer { ) Begin { + write-warning "This cmdlet is deprecated and will be removed in future release. Use VMware.VimAutomation.Storage\Add-KeyManagementServer instead" # Confirm the connected VIServer is vCenter Server ConfirmIsVCenter @@ -1546,13 +1475,6 @@ Function Remove-KMServer { .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -1566,6 +1488,7 @@ Function Remove-KMServer { ) Begin { + write-warning "This cmdlet is deprecated and will be removed in future release. Use VMware.VimAutomation.Storage\Remove-KeyManagementServer instead" # Confirm the connected VIServer is vCenter Server ConfirmIsVCenter @@ -1623,15 +1546,9 @@ Function Get-KMSCluster { .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> + write-warning "This cmdlet is deprecated and will be removed in future release. Use VMware.VimAutomation.Storage\Get-KmsCluster instead" # Confirm the connected VIServer is vCenter Server ConfirmIsVCenter @@ -1661,14 +1578,6 @@ Function Get-KMSClusterInfo { .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 - #> [CmdLetBinding()] @@ -1679,6 +1588,7 @@ Function Get-KMSClusterInfo { ) Begin { + write-warning "This cmdlet is deprecated and will be removed in future release. Use VMware.VimAutomation.Storage\Get-KmsCluster instead" # Confirm the connected VIServer is vCenter Server ConfirmIsVCenter @@ -1714,13 +1624,6 @@ Function Get-KMServerInfo { .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -1731,6 +1634,7 @@ Function Get-KMServerInfo { ) Begin { + write-warning "This cmdlet is deprecated and will be removed in future release. Use VMware.VimAutomation.Storage\Get-KeyManagementServer instead" # Confirm the connected VIServer is vCenter Server ConfirmIsVCenter @@ -1775,13 +1679,6 @@ Function Get-KMServerStatus { .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -1846,15 +1743,9 @@ Function Get-DefaultKMSCluster { .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> + write-warning "This cmdlet is deprecated and will be removed in future release. Use VMware.VimAutomation.Storage\Get-KmsCluster instead" # Confirm the connected VIServer is vCenter Server ConfirmIsVCenter @@ -1883,13 +1774,6 @@ Function Set-DefaultKMSCluster { .NOTES Author : Baoyin Qiao. Author email : bqiao@vmware.com - Version : 1.0 - - ==========Tested Against Environment========== - VMware vSphere Hypervisor(ESXi) Version : 6.5 - VMware vCenter Server Version : 6.5 - PowerCLI Version : PowerCLI 6.5 - PowerShell Version : 3.0 #> [CmdLetBinding()] @@ -1899,6 +1783,7 @@ Function Set-DefaultKMSCluster { [String] $KMSClusterId ) + write-warning "This cmdlet is deprecated and will be removed in future release. Use VMware.VimAutomation.Storage\Set-KmsCluster instead" # Confirm the connected VIServer is vCenter Server ConfirmIsVCenter @@ -1910,6 +1795,353 @@ Function Set-DefaultKMSCluster { $CM.MarkDefault($ProviderId) } +Function Set-VMCryptoUnlock { + <# + .SYNOPSIS + This cmdlet unlocks a locked vm + + .DESCRIPTION + This cmdlet unlocks a locked vm + + .PARAMETER VM + Specifies the VM you want to unlock + + .EXAMPLE + PS C:\> Get-VM |where {$_.locked}| Set-VMCryptoUnlock + + Unlock all locked vms + + .NOTES + Author : Fangying Zhang + Author email : fzhang@vmware.com + #> + + [CmdLetBinding()] + + param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]]$VM + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + } + + Process { + foreach ($thisvm in $vm) { + if (!$thisvm.encrypted) { + write-warning "$thisvm is not encrypted, will skip $thisvm" + continue + } + if (!$thisvm.Locked) { + write-warning "$thisvm may not be locked!" + # $thisvm.locked could be false on old 6.5.0 build (bug 1931370), so do not skip $thisvm + } + write-verbose "try to CryptoUnlock $thisvm" + $thisvm.ExtensionData.CryptoUnlock() + } + } +} + +Function Add-Vtpm { + <# + .SYNOPSIS + This cmdlet adds a Virtual TPM to the specified VM. + + .DESCRIPTION + This cmdlet adds a Virtual TPM to the specified VM. + + .PARAMETER VM + Specifies the VM you want to add Virtual TPM to. + + .EXAMPLE + C:\PS>$vm1 = Get-VM -Name win2016 + C:\PS>Add-Vtpm $vm1 + + Encrypts $vm1's VM home and adds Virtual TPM + + .NOTES + If VM home is already encrypted, the cmdlet will add a Virtual TPM to the VM. + If VM home is not encrypted, VM home will be encrypted and Virtual TPM will be added. + + .NOTES + Author : Chong Yeo. + Author email : cyeo@vmware.com + #> + [CmdLetBinding()] + + param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + } + Process { + $deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec + $deviceChange.operation = "add" + $deviceChange.device = new-object VMware.Vim.VirtualTPM + $VMCfgSpec = New-Object VMware.Vim.VirtualMachineConfigSpec + $VMCfgSpec.DeviceChange = $deviceChange + + return $VM.ExtensionData.ReconfigVM_task($VMCfgSpec) + } +} + +Function Remove-Vtpm { + <# + .SYNOPSIS + This cmdlet removes a Virtual TPM from the specified VM. + + .DESCRIPTION + This cmdlet removes a Virtual TPM from the specified VM. + + .PARAMETER VM + Specifies the VM you want to remove Virtual TPM from. + + .EXAMPLE + C:\PS>$vm1 = Get-VM -Name win2016 + C:\PS>Remove-Vtpm $vm1 + + .EXAMPLE + C:\PS>Get-VM -Name win2016 |Remove-Vtpm + + Remove Virtual TPM from VM named win2016 + + .NOTES + Removing VirtualTPM will render all encrypted data on this VM unrecoverable. + VM home encryption state will be returned to the original state before Virtual TPM is added + + .NOTES + Author : Chong Yeo. + Author email : cyeo@vmware.com + #> + [CmdLetBinding(SupportsShouldProcess=$true, ConfirmImpact = "High")] + + param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + } + Process { + $message = "Removing Virtual TPM will render all encrypted data on this VM unrecoverable" + if ($PSCmdlet.ShouldProcess($message, $message + "`n Do you want to proceed", "WARNING")) { + $deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec + $deviceChange.operation = "remove" + $deviceChange.device = $vtpm + $VMCfgSpec = New-Object VMware.Vim.VirtualMachineConfigSpec + $VMCfgSpec.DeviceChange = $deviceChange + return $VM.ExtensionData.ReconfigVM_task($VMCfgSpec) + } + } +} + +Function Get-VtpmCsr { + <# + .SYNOPSIS + This cmdlet gets certficate signing requests(CSR) from Virtual TPM. + + .DESCRIPTION + This cmdlet gets certficate signing requests(CSR) from Virtual TPM. + The CSR is a ComObject X509enrollment.CX509CertificateRequestPkcs10 + + .PARAMETER VM + Specifies the VM you want to get the CSRs Virtual TPM from. + + .PARAMETER KeyType [RSA | ECC] + Specify that only get CSR with public key RSA algorithm. + If none is specified, both CSR will get returned + + .EXAMPLE + C:\PS>$vm1 = Get-VM -Name win2016 + C:\PS>Get-VtpmCsr $vm1 -KeyType RSA + + .NOTES + Both RSA and ECC CSRs objects will be returned. If ECC or RSA is specified, + only the corresponding object will be returned + + .NOTES + Author : Chong Yeo. + Author email : cyeo@vmware.com + #> + [CmdLetBinding()] + + param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM, + + [Parameter(Mandatory=$False)] + [String]$KeyType + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + } + + process { + # Get vTPM from VM + $vtpm = $VM.ExtensionData.Config.Hardware.Device |Where {$_ -is [VMware.Vim.VirtualTPM]} + + # Check if vTPM is already present + if (!$vtpm) { + Write-Error "$VM does not contains a Virtual TPM" + return + } + + $CSRs = @() + foreach ($csrArray in $vtpm.EndorsementKeyCertificateSigningRequest) { + $csrString = [System.Convert]::ToBase64String($csrArray) + $csr = New-Object -ComObject X509enrollment.CX509CertificateRequestPkcs10 + + #decode a base64 string into a CSR object + $csr.InitializeDecode($csrString,6) + if ($keyType) { + if ($csr.PublicKey.Algorithm.FriendlyName -eq $KeyType){ + return $csr + } + } else { + $CSRs += $csr + } + } + return $CSRs + } +} + +Function Set-VtpmCert{ + <# + .SYNOPSIS + This cmdlet replaces certificates of Virtual TPM in the specified VM. + + .DESCRIPTION + This cmdlet replaces certificates to Virtual TPM in the specified VM. + + .PARAMETER VM + Specifies the VM with Virtual TPM where you want to replace the certificates to. + + .PARAMETER Cert + Specifies the certificate object (System.Security.Cryptography.X509Certificates.X509Certificate) + + .EXAMPLE + C:\PS>$vm1 = Get-VM -Name win2016 + C:\PS>Set-VtpmCert $vm1 $certObj + + .EXAMPLE + C:\PS>Get-VM -Name win2016 | Set-VtpmCert $certObj + + Replace the appropriate certificate specified + + .NOTES + Only RSA or ECC certs will be overwritten + + .NOTES + Author : Chong Yeo. + Author email : cyeo@vmware.com + #> + [CmdLetBinding()] + + param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]$VM, + + [Parameter(Mandatory=$True)] + [System.Security.Cryptography.X509Certificates.X509Certificate] $Cert + ) + + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + } + + process { + # Get vTPM from VM + $vtpm = $VM.ExtensionData.Config.Hardware.Device |Where {$_ -is [VMware.Vim.VirtualTPM]} + + #check if vTPM is already present + if (!$vtpm) { + Write-Error "$VM does not contains a Virtual TPM" + return + } + + $certOid = New-Object System.Security.Cryptography.Oid($Cert.GetKeyAlgorithm()) + + # Check which certificate to overwrite + $certLocation = GetKeyIndex $vtpm.EndorsementKeyCertificate $certOid.FriendlyName + if ($certLocation -eq -1) { + Write-Error "No Certificate with Matching Algorithm $($certOid.FriendlyName) found" + return + } + + $vtpm.EndorsementKeyCertificate[$certLocation] = $cert.GetRawCertData() + $deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec + $deviceChange.Operation = "edit" + $deviceChange.Device = $vtpm + $VMCfgSpec = New-Object VMware.Vim.VirtualMachineConfigSpec + $VMCfgSpec.DeviceChange = $deviceChange + + return $VM.ExtensionData.ReconfigVM_task($VMCfgSpec) + } +} + +Function Get-VtpmCert{ + <# + .SYNOPSIS + This cmdlet gets certificates of Virtual TPM in the specified VM. + + .DESCRIPTION + This cmdlet gets certificates of Virtual TPM in the specified VM. + + .PARAMETER VM + Specifies the VM with Virtual TPM where you want to get the certificate from + + .EXAMPLE + C:\PS>$vm1 = Get-VM -Name win2016 + C:\PS>$certs = Get-VtpmCert $vm1 + + .NOTES + An array of certificate object (System.Security.Cryptography.X509Certificates.X509Certificate) + will be returned + + .NOTES + Author : Chong Yeo. + Author email : cyeo@vmware.com + #> + [CmdLetBinding()] + param ( + [Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)] + [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine] $VM + ) + Begin { + # Confirm the connected VIServer is vCenter Server + ConfirmIsVCenter + } + Process { + # Get vTPM from VM + $vtpm = $VM.ExtensionData.Config.Hardware.Device |Where {$_ -is [VMware.Vim.VirtualTPM]} + + # check if vTPM is already present + if (!$vtpm) { + Write-Error "$VM does not contain a Virtual TPM" + return + } + + $certs = @() + $vtpm.EndorsementKeyCertificate|foreach { + $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate + $cert.Import($_) + $certs += $cert + } + return $certs + } +} + Function ConfirmIsVCenter{ <# .SYNOPSIS @@ -2104,4 +2336,49 @@ Function GetCryptoManager { } } +Function GetKeyIndex{ + <# + .SYNOPSIS + This cmdlet returns the index to the key with a matching algorithm as the KeyType parameter + + .DESCRIPTION + This cmdlet returns the index to the key with a matching algorithm as the KeyType parameter + + .PARAMETER Certs + Specifies the list of certificats. Expected format is byte[][] + + .PARAMETER KeyType + Specifies the keytype to search for + + .EXAMPLE + C:\PS>$keyIndex = GetKeyIndex $Certs RSA + C:\PS>$keyIndex = GetKeyIndex $Certs ECC + + .NOTES + Author : Chong Yeo. + Author email : cyeo@vmware.com + #> + + [CmdLetBinding()] + + param ( + [Parameter(Mandatory=$True)] + [byte[][]] $Certs, + + [Parameter(Mandatory=$True)] + [String] $KeyType + ) + process { + for ($i=0;$i -lt $Certs.Length; $i++) { + $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate + $cert.Import($Certs.Get($i)) + $certType = New-Object System.Security.Cryptography.Oid($cert.GetKeyAlgorithm()) + if ( $certType.FriendlyName -eq $keyType) { + return $i + } + } + return -1 + } +} + Export-ModuleMember *-* diff --git a/Modules/Validate-ESXiPackages/Validate-ESXiPackages.psm1 b/Modules/Validate-ESXiPackages/Validate-ESXiPackages.psm1 new file mode 100644 index 0000000..ccf5d85 --- /dev/null +++ b/Modules/Validate-ESXiPackages/Validate-ESXiPackages.psm1 @@ -0,0 +1,82 @@ +function Validate-ESXiPackages { + <# + .DESCRIPTION + Compares all ESXi Host VIBs within a vSphere with a reference Hosts. + + .NOTES + File Name : Validate-ESXiPackages.ps1 + Author : Markus Kraus + Version : 1.0 + State : Ready + + Tested Against Environment: + + vSphere Version: 6.0 U2, 6.5 U1 + PowerCLI Version: PowerCLI 10.0.0 build 7895300 + PowerShell Version: 4.0 + OS Version: Windows Server 2012 R2 + + .LINK + https://mycloudrevolution.com/ + + .EXAMPLE + Validate-ESXiPackages -Cluster (Get-Cluster) -RefernceHost (Get-VMHost | Select-Object -First 1) + + .PARAMETER Cluster + vSphere Cluster to verify + + .PARAMETER RefernceHost + The VIB Reference ESXi Host + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory=$True, ValueFromPipeline=$True, HelpMessage="vSphere Cluster to verify")] + [ValidateNotNullorEmpty()] + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.ComputeResourceImpl] $Cluster, + [Parameter(Mandatory=$True, ValueFromPipeline=$false, HelpMessage="The VIB Reference ESXi Host")] + [ValidateNotNullorEmpty()] + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl] $RefernceHost + ) + + Process { + + #region: Get reference VIBs + $EsxCli2 = Get-ESXCLI -VMHost $RefernceHost -V2 + $RefernceVibList = $esxcli2.software.vib.list.invoke() + #endregion + + #region: Compare reference VIBs + $MyView = @() + foreach ($VmHost in ($Cluster | Get-VMHost)) { + + $EsxCli2 = Get-ESXCLI -VMHost $VmHost -V2 + $VibList = $esxcli2.software.vib.list.invoke() + [Array]$VibDiff = Compare-Object -ReferenceObject $RefernceVibList.ID -DifferenceObject $VibList.ID + + if($VibDiff.Count -gt 0) { + $VibDiffSideIndicator = @() + foreach ($Item in $VibDiff) { + $VibDiffSideIndicator += $($Item.SideIndicator + " " + $Item.InputObject) + } + } + else { + $VibDiffSideIndicator = $null + } + + $Report = [PSCustomObject] @{ + Host = $VmHost.Name + Version = $VmHost.Version + Build = $VmHost.Build + VibDiffCount = $VibDiff.Count + VibDiff = $VibDiff.InputObject + VibDiffSideIndicator = $VibDiffSideIndicator + } + $MyView += $Report + + } + #region: Compare reference VIBs + + $MyView + } +} \ No newline at end of file diff --git a/Modules/apply-hardening.psm1 b/Modules/apply-hardening/apply-hardening.psm1 similarity index 100% rename from Modules/apply-hardening.psm1 rename to Modules/apply-hardening/apply-hardening.psm1 diff --git a/Modules/rCisTag/Examples/01-Get.ps1 b/Modules/rCisTag/Examples/01-Get.ps1 new file mode 100644 index 0000000..52cf4de --- /dev/null +++ b/Modules/rCisTag/Examples/01-Get.ps1 @@ -0,0 +1,17 @@ +# Fetch Cis Server hostname and credentials +.\CisConfig.ps1 + + +Connect-rCisServer -Server $cisServer -User $cisUser -Password $cisPswd + +# Get Tag information +Get-rCisTag + +# Get Tag Category information +Get-rCisTagCategory + +# Get Tag Assignment information +Get-rCisTagAssignment + +Disconnect-rCisServer -Server $cisServer -Confirm:$false + \ No newline at end of file diff --git a/Modules/rCisTag/Examples/02-New.ps1 b/Modules/rCisTag/Examples/02-New.ps1 new file mode 100644 index 0000000..7fe9af2 --- /dev/null +++ b/Modules/rCisTag/Examples/02-New.ps1 @@ -0,0 +1,14 @@ +# Fetch Cis Server hostname and credentials +.\CisConfig.ps1 + +Connect-rCisServer -Server $cisServer -User $cisUser -Password $cisPswd + +New-rCisTagCategory -Name MyCat1 -Cardinality Single -Description 'Test Tag Category' -EntityType 'VirtualMachine' +New-rCisTag -Name MyTag1 -Category MyCat1 -Description 'Test Tag' +$vm = Get-VM | Get-Random +New-rCisTagAssignment -Entity $vm -Tag MyTag1 + +Get-rCisTagAssignment -Tag MyTag1 + +Disconnect-rCisServer -Server $cisServer -Confirm:$false + \ No newline at end of file diff --git a/Modules/rCisTag/Examples/03-Set.ps1 b/Modules/rCisTag/Examples/03-Set.ps1 new file mode 100644 index 0000000..35d26b0 --- /dev/null +++ b/Modules/rCisTag/Examples/03-Set.ps1 @@ -0,0 +1,11 @@ +# Fetch Cis Server hostname and credentials +.\CisConfig.ps1 + +Connect-rCisServer -Server $cisServer -User $cisUser -Password $cisPswd + +Get-rCisTag -Name MyTag1 | Set-rCisTag -Name MyNewTag1 -Description 'Name changed' + +Get-rCisTagCategory -Name MyCat1 | Set-rCisTagCategory -Cardinality Multiple -Name MyNewCat1 -Description 'Name changed' + +Disconnect-rCisServer -Server $cisServer -Confirm:$false + \ No newline at end of file diff --git a/Modules/rCisTag/Examples/04-Remove.ps1 b/Modules/rCisTag/Examples/04-Remove.ps1 new file mode 100644 index 0000000..2f684f9 --- /dev/null +++ b/Modules/rCisTag/Examples/04-Remove.ps1 @@ -0,0 +1,13 @@ +# Fetch Cis Server hostname and credentials +.\CisConfig.ps1 + +Connect-rCisServer -Server $cisServer -User $cisUser -Password $cisPswd + +Get-rCisTagAssignment -Tag MyNewTag1 | Remove-rCisTagAssignment -Confirm:$false + +Get-rCisTag -Name MyNewTag1 | Remove-rCisTag -Confirm:$false + +Get-rCisTagCategory -Name MyNewCat1 | Remove-rCisTagCategory -Confirm:$false + +Disconnect-rCisServer -Server $cisServer -Confirm:$false + \ No newline at end of file diff --git a/Modules/rCisTag/Examples/05-Tag-Datastore.ps1 b/Modules/rCisTag/Examples/05-Tag-Datastore.ps1 new file mode 100644 index 0000000..f0ad343 --- /dev/null +++ b/Modules/rCisTag/Examples/05-Tag-Datastore.ps1 @@ -0,0 +1,20 @@ +# Fetch Cis Server hostname and credentials +.\CisConfig.ps1 + +Connect-rCisServer -Server $cisServer -User $cisUser -Password $cisPswd + +$catName = 'Homelab' + +# Clean up +Get-rCisTagCategory -Name $catName | Remove-rCisTagCategory -Confirm:$false + +# Tag all datastores with their type +New-rCisTagCategory -Name HomeLab -Description 'Homelab datastores' -Cardinality Single -EntityType 'Datastore' | +New-rCisTag -Name 'VMFS','NFS' -Description 'Datastore type' + +Get-Cluster -Name Cluster1 | Get-Datastore | %{ + New-rCisTagAssignment -Entity $_ -Tag "$($_.Type)" +} + +Disconnect-rCisServer -Server $cisServer -Confirm:$false + \ No newline at end of file diff --git a/Modules/rCisTag/Examples/CisConfig.ps1 b/Modules/rCisTag/Examples/CisConfig.ps1 new file mode 100644 index 0000000..de72021 --- /dev/null +++ b/Modules/rCisTag/Examples/CisConfig.ps1 @@ -0,0 +1,3 @@ +$cisServer = 'vcsa.my.domain' +$cisUser = 'administrator@vsphere.local' +$cisPswd = 'VMware1!' diff --git a/Modules/rCisTag/MITLicense.txt b/Modules/rCisTag/MITLicense.txt new file mode 100644 index 0000000..b4eaf48 --- /dev/null +++ b/Modules/rCisTag/MITLicense.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) since 2015 Luc Dekens, Matt Boren + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Modules/rCisTag/README.md b/Modules/rCisTag/README.md new file mode 100644 index 0000000..4160fdc --- /dev/null +++ b/Modules/rCisTag/README.md @@ -0,0 +1,15 @@ +# rCisTag + +A module with cmdlets to provide CRUD functions to +* Tags +* Tag Categories +* Tag Assignments + +The cmdlets use the Cis REST API + +## History + +* Author : **Luc Dekens** +* Release : + * **0.9.0** First draft + \ No newline at end of file diff --git a/Modules/rCisTag/en-US/about_rCISTag.Help.txt b/Modules/rCisTag/en-US/about_rCISTag.Help.txt new file mode 100644 index 0000000..f3b393f --- /dev/null +++ b/Modules/rCisTag/en-US/about_rCISTag.Help.txt @@ -0,0 +1,26 @@ +TOPIC + about_rCISTag + +SHORT DESCRIPTION + The rCisTag module provides CRUD functions to work with vSphere Tags + +LONG DESCRIPTION + The CisTag module provides CRUD functions to work with vSphere Tags. + The functions in the module are based on the REST API + +NOTE + The module requires PowerShell 5.0 + +TROUBLESHOOTING NOTE + + + +EXAMPLES + Get-rCisTag + +KEYWORDS + + + +SEE ALSO + Place related topics here. \ No newline at end of file diff --git a/Modules/rCisTag/en-US/rCISTag-help.xml b/Modules/rCisTag/en-US/rCISTag-help.xml new file mode 100644 index 0000000..023f48c --- /dev/null +++ b/Modules/rCisTag/en-US/rCISTag-help.xml @@ -0,0 +1,1793 @@ + + + + + Connect-rCisServer + Connect + rCisServer + + Make a connection to the CIS service on the vCenter + + + + Make a connection to the CIS service on the vCenter. Credentials need to be provided. + + + + Connect-rCisServer + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + Credential + + A PSCredential object with a user and password that is allowed to connect to the CIS service. + + PSCredential + + PSCredential + + + None + + + Fiddler + + Connects to a Fiddler instance on the station where the cmdlet is executed. Used for debugging purposes. + + + SwitchParameter + + + False + + + Proxy + + When the vCenter is located behind a proxy, specify the proxy. + + String + + String + + + None + + + Server + + The hostname of the vCenter + + String + + String + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + + Connect-rCisServer + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + Fiddler + + Connects to a Fiddler instance on the station where the cmdlet is executed. Used for debugging purposes. + + + SwitchParameter + + + False + + + Password + + The user's password + + String + + String + + + None + + + Proxy + + When the vCenter is located behind a proxy, specify the proxy. + + String + + String + + + None + + + Server + + The hostname of the vCenter + + String + + String + + + None + + + User + + The user to connect to the CIS service + + String + + String + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + SwitchParameter + + SwitchParameter + + + False + + + Credential + + A PSCredential object with a user and password that is allowed to connect to the CIS service. + + PSCredential + + PSCredential + + + None + + + Fiddler + + Connects to a Fiddler instance on the station where the cmdlet is executed. Used for debugging purposes. + + SwitchParameter + + SwitchParameter + + + False + + + Password + + The user's password + + String + + String + + + None + + + Proxy + + When the vCenter is located behind a proxy, specify the proxy. + + String + + String + + + None + + + Server + + The hostname of the vCenter + + String + + String + + + None + + + User + + The user to connect to the CIS service + + String + + String + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + SwitchParameter + + SwitchParameter + + + False + + + + + + System.Management.Automation.PSCredential + + + + + + + + + + System.Object + + + + + + + + + + + + + + Example 1 + PS C:\> Connect-rCisServer -Server 'vcsa.domain.org' -User 'administrator@vsphere.local' -Password 'vSphere1!' + + Connect to the CIS service on vCenter vcsa.domain.org with user administrator@vsphere.local + + + + Example 2 + PS C:\> Connect-rCisServer -Server 'vcsa.domain.org' -Credential $cred + + Connect to the CIS service on vCenter vcsa.domain.org with the PSCredential stored in $cred + + + + + + + + Disconnect-rCisServer + Disconnect + rCisServer + + Closes the open connection to the CIS REST API + + + + Closes the open connection to the Cis REST API on the host passed on the Server parameter. + + + + Disconnect-rCisServer + + Server + + The name of the server on which the Cis REST API was opened. + + String + + String + + + None + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + SwitchParameter + + SwitchParameter + + + False + + + Server + + The name of the server on which the Cis REST API was opened. + + String + + String + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + SwitchParameter + + SwitchParameter + + + False + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + Example 1 + PS C:\> Disconnect-rCisServer -Server 'vcsa.domain.org' + + Closes the connection to the server vcsa.domain.org + + + + + + + + Get-rCisTag + Get + rCisTag + + Retrieves Tags from vCenter + + + + Return Tags found on the vCenter. + + + + Get-rCisTag + + Category + + The returned tags are restricted to the category with name passed on this parameter + + PSObject[] + + PSObject[] + + + None + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + Name + + Return the tag(s) with this/these name(s) + + String[] + + String[] + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + + Get-rCisTag + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + Id + + Return the tag with the specific ID + + String[] + + String[] + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + + + + Category + + The returned tags are restricted to the category with name passed on this parameter + + PSObject[] + + PSObject[] + + + None + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + SwitchParameter + + SwitchParameter + + + False + + + Id + + Return the tag with the specific ID + + String[] + + String[] + + + None + + + Name + + Return the tag(s) with this/these name(s) + + String[] + + String[] + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + SwitchParameter + + SwitchParameter + + + False + + + + + + System.Management.Automation.PSObject[] + + + + + + + + + + System.Object + + + + + + + + + + + + + + Example 1 + PS C:\> Get-rCisTag -Name MyTag + + Return the tags that are named MyTag + + + + Example 2 + PS C:\> Get-rCisTag -Name MyTag -Category MyCategory + + Return the tags that are named MyTag in the category MyCategory + + + + Example 3 + PS C:\> Get-rCisTag -Id urn:vmomi:InventoryServiceTag:385c90a7-e4ad-49af-a0a5-66fc5fedb254:GLOBAL + + Return the tag with the Id urn:vmomi:InventoryServiceTag:385c90a7-e4ad-49af-a0a5-66fc5fedb254:GLOBAL + + + + Example 4 + PS C:\> Get-rCisTagCategory -Name MyCat | Get-rCisTag + + Return all the Tags in TagCategory MyCat + + + + Example 5 + PS C:\> Get-rCisTag -Name MyTag1,MyTag2 + + Return all the Tags named MyTag1 and MyTag2 + + + + + + + + Get-rCisTagAssignment + Get + rCisTagAssignment + + Return Tag assignments + + + + Return information about all or specific Tag assignments + + + + Get-rCisTagAssignment + + Entity + + Return Tag assignments made to this entity + + PSObject[] + + PSObject[] + + + None + + + Tag + + Return Tag assignments for this Tag + + PSObject[] + + PSObject[] + + + None + + + Category + + Return only Tag assignments for Tags from this specific Category + + PSObject[] + + PSObject[] + + + None + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + + + + Category + + Return only Tag assignments for Tags from this specific Category + + PSObject[] + + PSObject[] + + + None + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + SwitchParameter + + SwitchParameter + + + False + + + Entity + + Return Tag assignments made to this entity + + PSObject[] + + PSObject[] + + + None + + + Tag + + Return Tag assignments for this Tag + + PSObject[] + + PSObject[] + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + SwitchParameter + + SwitchParameter + + + False + + + + + + System.Management.Automation.PSObject[] + + + + + + + + + + System.Object + + + + + + + + + + + + + + Example 1 + PS C:\> Get-rCisTagAssignment + + Return all Tag assignments + + + + Example 2 + PS C:\> Get-rCisTagAssignment -Entity MyVM + + Return all Tag assignments to entity MyVM + + + + Example 3 + PS C:\> Get-rCisTagAssignment -Entity MyVM -Tag MyTag + + Return the Tag assignment to entity MyVM for a Tag with the name MyTag + + + + Example 4 + PS C:\> Get-rCisTagAssignment -Entity MyVM -Category MyCat + + Return all Tag assignments to entity MyVM with Tags from the Tag Category named MyCat + + + + Example 5 + PS C:\> Get-rCisTagAssignment -Category MyCat + + Return all Tag assignments for all Tags from the Tag Category MyCat + + + + Example 6 + PS C:\> Get-VM MyVM | Get-rCisTagAssignment + + Return all Tag assignments made to entity MyVM + + + + + + + + Get-rCisTagCategory + Get + rCisTagCategory + + Return one or more Tag Categories + + + + Return specific Tag Categories defined on the vCenter. The returned Tag Categories can be filtered by the parameters. + + + + Get-rCisTagCategory + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + Id + + The Tag Category Id + + String[] + + String[] + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + + Get-rCisTagCategory + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + Name + + The name of the Tag Category + + String[] + + String[] + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + SwitchParameter + + SwitchParameter + + + False + + + Id + + The Tag Category Id + + String[] + + String[] + + + None + + + Name + + The name of the Tag Category + + String[] + + String[] + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + SwitchParameter + + SwitchParameter + + + False + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + Example 1 + PS C:\> Get-rCisTagCategory + + Return all Tag Categories + + + + Example 2 + PS C:\> Get-rCisTagCategory -Name Cat1,Cat2 + + Return the Tag Categories named Cat1 and Cat2 + + + + Example 3 + PS C:\> Get-rCisTagCategory -Id urn:vmomi:InventoryServiceCategory:925d8ae1-6b6e-403a-9a12-e78e14b1cdd4:GLOBAL + + Return the Tag Category with Id urn:vmomi:InventoryServiceCategory:925d8ae1-6b6e-403a-9a12-e78e14b1cdd4:GLOBAL + + + + + + + + New-rCisTag + New + rCisTag + + Create a Tag + + + + Create a Tag in one or more Categories, optionally with a Description + + + + New-rCisTag + + Name + + The Name of the new Tag + + String[] + + String[] + + + None + + + Category + + The name of the Category in which to create the Tag + + PSObject + + PSObject + + + None + + + Description + + An optional Description for the Tag + + String + + String + + + None + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + + + + Category + + The name of the Category in which to create the Tag + + PSObject + + PSObject + + + None + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + SwitchParameter + + SwitchParameter + + + False + + + Description + + An optional Description for the Tag + + String + + String + + + None + + + Name + + The Name of the new Tag + + String[] + + String[] + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + SwitchParameter + + SwitchParameter + + + False + + + + + + System.Management.Automation.PSObject + + + + + + + + + + System.Object + + + + + + + + + + + + + + Example 1 + PS C:\> New-rCisTag -Name MyTag -Category MyCategory + + Creates a Tag named MyTag in the Category MyCategory + + + + Example 2 + PS C:\> New-rCisTag -Name MyTag + + Creates a Tag named MyTag in all Categories + + + + Example 3 + PS C:\> Get-rCisCategory -Name MyCategory | New-rCisTag -Name MyTag -Description 'Text' + + Creates a Tag named MyTag in the Category MyCategory. And add the Description 'Text' to the Tag. + + + + + + + + New-rCisTagAssignment + New + rCisTagAssignment + + Create a new Tag assignment to an Entity + + + + Create a Tag assignment to one more Entities + + + + New-rCisTagAssignment + + Tag + + The name of the Tag to assign to the entity + + String[] + + String[] + + + None + + + Entity + + The name of the Entity or the PowerCLI object for the entity + + PSObject[] + + PSObject[] + + + None + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + SwitchParameter + + SwitchParameter + + + False + + + Entity + + The name of the Entity or the PowerCLI object for the entity + + PSObject[] + + PSObject[] + + + None + + + Tag + + The name of the Tag to assign to the entity + + String[] + + String[] + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + SwitchParameter + + SwitchParameter + + + False + + + + + + System.Management.Automation.PSObject[] + + + + + + + + + + System.Object + + + + + + + + + + + + + + Example 1 + PS C:\> New-rCisTagAssignment -Tag MyTag -Entity MyVM + + Assign the Tag MyTag to the entity MyVM + + + + Example 2 + PS C:\> Get-VM | New-rCisTagAssignment -Tag MyTag + + Assign the Tag MyTag to all VMs + + + + + + + + New-rCisTagCategory + New + rCisTagCategory + + Create a new Tag Category + + + + Creates a new Tag Category. The user can define the Cardinality of the Tag Catgeory (default Single), add a description to the Tag Category and specify for which entity types the Tag Category can be used. + + + + New-rCisTagCategory + + Name + + The name of the new Tag Category + + String[] + + String[] + + + None + + + Cardinality + + Defines the Cardinality of the Tags in this Tag Category. Possible values are Single (default) and Multiple. + + + Single + Multiple + + String + + String + + + None + + + Description + + A description that is attached to the Tag Category + + String + + String + + + None + + + EntityType + + The type of Entity/Entities to which Tags in this Tag Category can be attached. + + String[] + + String[] + + + None + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + + + + Cardinality + + Defines the Cardinality of the Tags in this Tag Category. Possible values are Single (default) and Multiple. + + String + + String + + + None + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + SwitchParameter + + SwitchParameter + + + False + + + Description + + A description that is attached to the Tag Category + + String + + String + + + None + + + EntityType + + The type of Entity/Entities to which Tags in this Tag Category can be attached. + + String[] + + String[] + + + None + + + Name + + The name of the new Tag Category + + String[] + + String[] + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + SwitchParameter + + SwitchParameter + + + False + + + + + + None + + + + + + + + + + System.Object + + + + + + + + + + + + + + Example 1 + PS C:\> New-rCisTagCategory -Name MyCat + + Create a new Tag Category named MyCat + + + + Example 2 + PS C:\> New-rCisTagCategory -Name MyCat -Cardinality Multiple -Description 'My Category' + + Create a new Tag Category named MyCat, with a Cardinality of Multiple and a description. + + + + + + + + Remove-rCisTag + Remove + rCisTag + + Remove a Tag + + + + Remove one or more Tags from the environment + + + + Remove-rCisTag + + Tag + + {{Fill Tag Description}} + + PSObject[] + + PSObject[] + + + None + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + + Remove-rCisTag + + Confirm + + Prompts you for confirmation before running the cmdlet. + + + SwitchParameter + + + False + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + + SwitchParameter + + + False + + + Id + + {{Fill Id Description}} + + String[] + + String[] + + + None + + + + + + Confirm + + Prompts you for confirmation before running the cmdlet. + + SwitchParameter + + SwitchParameter + + + False + + + Tag + + {{Fill Tag Description}} + + PSObject[] + + PSObject[] + + + None + + + WhatIf + + Shows what would happen if the cmdlet runs. The cmdlet is not run. + + SwitchParameter + + SwitchParameter + + + False + + + Id + + {{Fill Id Description}} + + String[] + + String[] + + + None + + + + + + System.Management.Automation.PSObject[] + + + + + + + + + + System.Object + + + + + + + + + + + + + + Example 1 + PS C:\> Remove-rCisTag -Name MyTag + + Remove the Tag named MyTag + + + + Example 2 + PS C:\> Remove-rCisTag -Name MyTag + + Remove the Tag named MyTag + + + + + + \ No newline at end of file diff --git a/Modules/rCisTag/rCISTag.psd1 b/Modules/rCisTag/rCISTag.psd1 new file mode 100644 index 0000000..a9099d9 Binary files /dev/null and b/Modules/rCisTag/rCISTag.psd1 differ diff --git a/Modules/rCisTag/rCISTag.psm1 b/Modules/rCisTag/rCISTag.psm1 new file mode 100644 index 0000000..c5ed676 --- /dev/null +++ b/Modules/rCisTag/rCISTag.psm1 @@ -0,0 +1,821 @@ +function Disable-SSLValidation{ +<# +.SYNOPSIS + Disables SSL certificate validation +.DESCRIPTION + Disable-SSLValidation disables SSL certificate validation by using reflection to implement the System.Net.ICertificatePolicy class. + + Author: Matthew Graeber (@mattifestation) + License: BSD 3-Clause +.NOTES + Reflection is ideal in situations when a script executes in an environment in which you cannot call csc.ese to compile source code. If compiling code is an option, then implementing System.Net.ICertificatePolicy in C# and Add-Type is trivial. +.LINK + http://www.exploit-monday.com +#> + + Set-StrictMode -Version 2 + + # You have already run this function + if ([System.Net.ServicePointManager]::CertificatePolicy.ToString() -eq 'IgnoreCerts') { Return } + + $Domain = [AppDomain]::CurrentDomain + $DynAssembly = New-Object System.Reflection.AssemblyName('IgnoreCerts') + $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) + $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('IgnoreCerts', $false) + $TypeBuilder = $ModuleBuilder.DefineType('IgnoreCerts', 'AutoLayout, AnsiClass, Class, Public, BeforeFieldInit', [System.Object], [System.Net.ICertificatePolicy]) + $TypeBuilder.DefineDefaultConstructor('PrivateScope, Public, HideBySig, SpecialName, RTSpecialName') | Out-Null + $MethodInfo = [System.Net.ICertificatePolicy].GetMethod('CheckValidationResult') + $MethodBuilder = $TypeBuilder.DefineMethod($MethodInfo.Name, 'PrivateScope, Public, Virtual, HideBySig, VtableLayoutMask', $MethodInfo.CallingConvention, $MethodInfo.ReturnType, ([Type[]] ($MethodInfo.GetParameters() | % {$_.ParameterType}))) + $ILGen = $MethodBuilder.GetILGenerator() + $ILGen.Emit([Reflection.Emit.Opcodes]::Ldc_I4_1) + $ILGen.Emit([Reflection.Emit.Opcodes]::Ret) + $TypeBuilder.CreateType() | Out-Null + + # Disable SSL certificate validation + [System.Net.ServicePointManager]::CertificatePolicy = New-Object IgnoreCerts +} + +function Invoke-vCisRest{ + param ( + [String]$Method, + [String]$Request, + [PSObject]$Body + ) + + Process + { + Write-Verbose -Message "$($MyInvocation.MyCommand.Name)" + Write-Verbose -Message "`t$($PSCmdlet.ParameterSetName)" + Write-Verbose -Message "`tCalled from $($stack = Get-PSCallStack; $stack[1].Command) at $($stack[1].Location)" + + Disable-SSLValidation + + $sRest = @{ + Uri = "https:/",$Script:CisServer.Server,'rest',$Request -join '/' + Method = $Method +# Body = &{if($Body){$Body}} + Body = &{if($Body){$Body | ConvertTo-Json -Depth 32}} + ContentType = 'application/json' + Headers = &{ + if($Script:CisServer.ContainsKey('vmware-api-session-id')){ + @{ + 'vmware-api-session-id' = "$($Script:CisServer.'vmware-api-session-id')" + } + } + else{ + @{ + Authorization = "$($Script:CisServer.AuthHeader)" + } + } + } + } + Try + { +# $result = Invoke-WebRequest @sRest + $result = Invoke-RestMethod @sRest + } + Catch + { + + } + $result + } +} + +function Connect-rCisServer{ + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] + param ( + [Parameter(Mandatory, Position = 1)] + [String]$Server, + [Parameter(Mandatory = $True,ValueFromPipeline = $True, Position = 2, ParameterSetName = 'Credential')] + [System.Management.Automation.PSCredential]$Credential, + [Parameter(Mandatory = $True, Position = 2, ParameterSetName = 'PlainText')] + [String]$User, + [Parameter(Mandatory = $True, Position = 3, ParameterSetName = 'PlainText')] + [String]$Password, + [string]$Proxy, + [Parameter(DontShow)] + [switch]$Fiddler = $false + ) + + Process + { + if ($Proxy) + { + if ($PSDefaultParameterValues.ContainsKey('*:Proxy')) + { + $PSDefaultParameterValues['*:Proxy'] = $Proxy + } + else + { + $PSDefaultParameterValues.Add('*:Proxy', $Proxy) + } + if ($PSDefaultParameterValues.ContainsKey('*:ProxyUseDefaultCredentials')) + { + $PSDefaultParameterValues['*:ProxyUseDefaultCredentials'] = $True + } + else + { + $PSDefaultParameterValues.Add('*:ProxyUseDefaultCredentials', $True) + } + } + if ($PSCmdlet.ParameterSetName -eq 'PlainText') + { + $sPswd = ConvertTo-SecureString -String $Password -AsPlainText -Force + $CisCredential = New-Object System.Management.Automation.PSCredential -ArgumentList ($User, $sPswd) + } + if ($PSCmdlet.ParameterSetName -eq 'Credential') + { + $CisCredential = $Credential + } + if ($Fiddler) + { + if (Get-Process -Name fiddler -ErrorAction SilentlyContinue) + { + if ($PSDefaultParameterValues.ContainsKey('Invoke-RestMethod:Proxy')) + { + $PSDefaultParameterValues['Invoke-RestMethod:Proxy'] = 'http://127.0.0.1:8888' + } + else + { + $PSDefaultParameterValues.Add('Invoke-RestMethod:Proxy', 'http://127.0.0.1:8888') + } + } + } + $Script:CisServer = @{ + Server = $Server + AuthHeader = &{ + $User = $CisCredential.UserName + $Password = $CisCredential.GetNetworkCredential().password + + $Encoded = [System.Text.Encoding]::UTF8.GetBytes(($User, $Password -Join ':')) + $EncodedPassword = [System.Convert]::ToBase64String($Encoded) + "Basic $($EncodedPassword)" + } + } + $sRest = @{ + Method = 'Post' + Request = 'com/vmware/cis/session' + } + If($PSCmdlet.ShouldProcess("CisServer $($Server)")) + { + $result = Invoke-vCisRest @sRest + + $Script:CisServer.Add('vmware-api-session-id',$result.value) + $Script:CisServer.Remove('AuthHeader') + } + } +} + +function Disconnect-rCisServer{ + [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory = $True, Position = 1)] + [String]$Server + ) + + Process + { + if($Server -ne $Script:CisServer.Server){ + Write-Warning "You are not connected to server $($Server)" + } + + $sRest = @{ + Method = 'Delete' + Request = 'com/vmware/cis/session' + } + If($PSCmdlet.ShouldProcess("CisServer $($Server)")) + { + $result = Invoke-vCisRest @sRest + $Script:CisServer.Remove('vmware-api-session-id') + } + } +} + +function Get-rCisTag{` + [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'Low', DefaultParameterSetName='Name')] + param ( + [Parameter(Position = 1, ParameterSetName='Name')] + [String[]]$Name, + [Parameter(Position = 2, ParameterSetName='Name',ValueFromPipeline = $true)] + [PSObject[]]$Category, + [Parameter(Mandatory = $True, Position = 1, ParameterSetName='Id')] + [String[]]$Id + ) + + Process + { + if($PSCmdlet.ParameterSetName -eq 'Name'){ + if($Category){ + $tagIds = $Category | %{ + $categoryIds = &{if($_ -is [string]){ + (Get-rCisTagCategory -Name $_).Id + } + else{ + $_.Id + }} + $categoryIds | %{ + # Get all tags in categories + $sRest = @{ + Method = 'Post' + Request = "com/vmware/cis/tagging/tag/id:$([uri]::EscapeDataString($_))?~action=list-tags-for-category" + } + (Invoke-vCisRest @sRest).value + } + } + } + else{ + $sRest = @{ + Method = 'Get' + Request = 'com/vmware/cis/tagging/tag' + } + $tagIds = (Invoke-vCisRest @sRest).value + } + } + else{ + $tagIds = $Id + } + + # Get category details + $out = @() + $tagIds | where{($PSCmdlet.ParameterSetName -eq 'Id' -and $Id -contains $_) -or $PSCmdlet.ParameterSetName -eq 'Name'} | %{ + $sRest = @{ + Method = 'Get' + Request = "com/vmware/cis/tagging/tag/id:$([uri]::EscapeDataString($_))" + } + $result = Invoke-vCisRest @sRest + + if($PSCmdlet.ParameterSetName -eq 'Id' -or ($PSCmdlet.ParameterSetName -eq 'Name' -and ($Name -eq $null -or $Name -contains $result.value.name))){ + $out += New-Object PSObject -Property @{ + Description = $result.value.description + Id = $result.value.id + Name = $result.value.name + Category = (Get-rCisTagCategory -Id $result.value.category_id).Name + Uid = "$($global:defaultviserver.Id)Tag=$($result.value.id)/" + Client = $global:defaultviserver.Client + } + } + } + $out | Select-Object Category,Description,Id,Name,Uid,Client + } + +} + +function Get-rCisTagCategory{ + [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'Low', DefaultParameterSetName='Name')] + param ( + [Parameter(Position = 1, ParameterSetName='Name')] + [String[]]$Name, + [Parameter(Mandatory = $True, Position = 1, ParameterSetName='Id')] + [String[]]$Id + ) + + Begin + { + $txtInfo = (Get-Culture).TextInfo + $entityTab = @{ + 'ClusterComputeResource' = 'Cluster' + 'DistributedVirtualSwitch' = 'DistributedSwitch' + 'VmwareDistributedVirtualSwitch' = 'DistributedSwitch' + 'HostSystem' = 'VMHost' + 'DistributedVirtualPortGroup' = 'DistributedPortGroup' + 'VirtualApp' = 'VApp' + 'StoragePod' = 'DatastoreCluster' + 'Network' = 'VirtualPortGroup' + } + } + + Process + { + if($PSCmdlet.ParameterSetName -eq 'Name'){ + # Get all categories + $sRest = @{ + Method = 'Get' + Request = 'com/vmware/cis/tagging/category' + } + $tagCategoryIds = (Invoke-vCisRest @sRest).value + } + else{ + $tagCategoryIds = $Id + } + + # Get category details + $out = @() + $tagCategoryids | where{($PSCmdlet.ParameterSetName -eq 'Id' -and $Id -contains $_) -or $PSCmdlet.ParameterSetName -eq 'Name'} | %{ + $sRest = @{ + Method = 'Get' + Request = "com/vmware/cis/tagging/category/id:$([uri]::EscapeDataString($_))" + } + $result = Invoke-vCisRest @sRest + if($PSCmdlet.ParameterSetName -eq 'Id' -or ($PSCmdlet.ParameterSetName -eq 'Name' -and ($Name -eq $null -or $Name -contains $result.value.name))){ + $out += New-Object PSObject -Property @{ + Description = $result.value.description + Cardinality = $txtInfo.ToTitleCase($result.value.cardinality.ToLower()) + EntityType = @(&{ + if($result.value.associable_types.Count -eq 0){'All'} + else{ + $result.value.associable_types | %{ + if($entityTab.ContainsKey($_)){ + $entityTab.Item($_) + } + else{$_} + } + }} | Sort-Object -Unique) + Id = $result.value.id + Name = $result.value.name + Uid = "$($global:defaultviserver.Id)TagCategory=$($result.value.id)/" + Client = $global:defaultviserver.Client + } + } + } + $out | Select-Object Description,Cardinality,EntityType,Id,Name,Uid,Client + } +} + +function Get-rCisTagAssignment{ + [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'Low')] + param ( + [parameter(Position = 1, ValueFromPipeline = $true)] + [PSObject[]]$Entity, + [parameter(Position = 2)] + [PSObject[]]$Tag, + [parameter(Position = 3)] + [PSObject[]]$Category + ) + + Begin + { + if($Category.Count -ne 0 -or $Tag.Count -ne 0){ + $tagIds = @((Get-rCisTag -Name $Tag -Category $Category).Id) + } + else{ + $tagIds = @((Get-rCisTag).Id) + } + $out = @() + } + + Process + { + foreach($ent in $Entity){ + if($ent -is [string]){ + $ent = Get-Inventory -Name $ent -ErrorAction SilentlyContinue + } + + $entMoRef = New-Object PSObject -Property @{ + type = $ent.ExtensionData.MoRef.Type + id = $ent.ExtensionData.MoRef.Value + } + $sRest = @{ + Method = 'Post' + Request = 'com/vmware/cis/tagging/tag-association?~action=list-attached-tags-on-objects' + Body = @{ + object_ids = @($entMoRef) + } + } + $tagObj = (Invoke-vCisRest @sRest).value + foreach($obj in @($tagObj)){ + foreach($tag in ($obj.tag_ids | where{$tagIds -contains $_})){ + $sMoRef = "$($obj.object_id.type)-$($obj.object_id.id)" + $out += New-Object PSObject -Property @{ + Entity = (Get-View -id $sMoRef -Property Name).Name + Tag = (Get-rCisTag -Id $tag).Name + Id = 'com.vmware.cis.tagging.TagAssociationModel' + Name = 'com.vmware.cis.tagging.TagAssociationModel' + Uid = "$($global:defaultviserver.Id)VirtualMachine=$($sMoRef)/TagAssignment=/Tag=$($tag.tag_id)/" + Client = $global:defaultviserver.Client + } + } + } + } + } + + End + { + if($out.Count -eq 0) + { + $sRest = @{ + Method = 'Post' + Request = 'com/vmware/cis/tagging/tag-association?~action=list-attached-objects-on-tags' + Body = @{ + tag_ids = $tagIds + } + } + $tagObj = (Invoke-vCisRest @sRest).value + $out = foreach($tag in @(($tagObj | where{$tagIds -contains $_.tag_id}))){ + foreach($obj in $tag.object_ids){ + $sMoRef = "$($obj.type)-$($obj.id)" + New-Object PSObject -Property @{ + Entity = (Get-View -id $sMoRef -Property Name).Name + Tag = (Get-rCisTag -Id $tag.tag_id).Name + Id = 'com.vmware.cis.tagging.TagAssociationModel' + Name = 'com.vmware.cis.tagging.TagAssociationModel' + Uid = "$($global:defaultviserver.Id)VirtualMachine=$($sMoRef)/TagAssignment=/Tag=$($tag.tag_id)/" + Client = $global:defaultviserver.Client + } + } + } + } + + $out | Select-Object Uid,Tag,Entity,Id,Name,Client + } +} + +function New-rCisTag{ + [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory=$true, Position = 1)] + [String[]]$Name, + [Parameter(Mandatory=$true, Position = 2,ValueFromPipeline = $true)] + [PSObject]$Category, + [Parameter(Position = 3)] + [string]$Description + ) + + Process + { + $out = @() + if($Category -is [String]){ + $Category = Get-rCisTagCategory -Name $Category + } + $Name | %{ + $sRest = @{ + Method = 'Post' + Request = 'com/vmware/cis/tagging/tag' + Body = @{ + create_spec = @{ + category_id = $Category.Id + name = $_ + description = $Description + } + } + } + $tagId = (Invoke-vCisRest @sRest).value + $out += New-Object PSObject -Property @{ + Category = $Category.Name + Description = $Description + Id = $tagId + Name = $_ + Uid = "$($global:defaultviserver.Id)Tag=$($tagId)/" + Client = $global:defaultviserver.Client + } + } + $out | Select-Object Category,Description,Id,Name,Uid,Client + } +} + +function New-rCisTagCategory{ + [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory=$true, Position = 1)] + [String[]]$Name, + [Parameter(Position = 2)] + [ValidateSet('Single','Multiple')] + [string]$Cardinality = 'Single', + [Parameter(Position = 3)] + [string]$Description, + [Parameter(Position = 4)] + [string[]]$EntityType + ) + + Process + { + $out = @() + $Name | %{ + $sRest = @{ + Method = 'Post' + Request = 'com/vmware/cis/tagging/category' + Body = @{ + create_spec = @{ + cardinality = $Cardinality.ToUpper() + associable_types = @($EntityType) + name = $_ + description = $Description + } + } + } + $categoryId = (Invoke-vCisRest @sRest).value + $out += New-Object PSObject -Property @{ + Description = $Description + Cardinality = $Cardinality + EntityType = @($EntityType) + Id = $categoryId + Name = $_ + Uid = "$($global:defaultviserver.Id)TagCategory=$($categoryId)/" + Client = $global:defaultviserver.Client + } + } + $out | Select-Object Description,Cardinality,EntityType,Id,Name,Uid,Client + } +} + +function New-rCisTagAssignment{ + [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory=$true, Position = 1)] + [String[]]$Tag, + [Parameter(Mandatory=$true,ValueFromPipeline = $true, Position = 2)] + [PSObject[]]$Entity + ) + + Process + { + $tagIds = @((Get-rCisTag -Name $Tag).Id) + $Entity = foreach($ent in $Entity){ + if($ent -is [string]){ + $ent = Get-Inventory -Name $ent -ErrorAction SilentlyContinue + } + $entMoRef = New-Object PSObject -Property @{ + type = $ent.ExtensionData.MoRef.Type + id = $ent.ExtensionData.MoRef.Value + } + foreach($tagId in $tagIds){ + $sRest = @{ + Method = 'Post' + Request = "com/vmware/cis/tagging/tag-association/id:$($tagId)?~action=attach" + Body = @{ + object_id = $entMoRef + } + } + Invoke-vCisRest @sRest + } + } + } + +# foreach($ent in +# if($Tag.Count -eq 1) +# { +# $tagId = (Get-rCisTag -Name $Tag).Id +# } +# elseif($Tag.Count -gt 1) +# { +# $tagIds = (Get-rCisTag -Name $Tag).Id +# } +# $Entity = foreach($ent in $Entity){ +# if($ent -is [string]){ +# Get-Inventory -Name $ent -ErrorAction SilentlyContinue +# } +# else{$ent} +# } +# +# if($Entity.Count -eq 1) +# { +# $entMoRef = New-Object PSObject -Property @{ +# type = $Entity[0].ExtensionData.MoRef.Type +# id = $Entity[0].ExtensionData.MoRef.Value +# } +# if($tag.Count -eq 1){ +# $sRest = @{ +# Method = 'Post' +# Request = "com/vmware/cis/tagging/tag-association/id:$($tagId)?~action=attach" +# Body = @{ +# object_id = $entMoRef +# } +# } +# Invoke-vCisRest @sRest +# } +# elseif($Tag.Count -gt 1){ +# $sRest = @{ +# Method = 'Post' +# Request = 'com/vmware/cis/tagging/tagassociation?~action=attach-multiple-tags-to-object' +# Body = @{ +# object_id = $entMoRef +# tag_ids = @($tagIds) +# } +# } +# Invoke-vCisRest @sRest +# } +# } +# elseif($Entity.Count -gt 1) +# { +# $entMorefs = $Entity | %{ +# New-Object PSObject -Property @{ +# type = $_.ExtensionData.MoRef.Type +# id = $_.ExtensionData.MoRef.Value +# } +# } +# if($tag.Count -eq 1){ +# $sRest = @{ +# Method = 'Post' +# Request = 'com/vmware/cis/tagging/tagassociation/id:$($tagId)?~action=attach-tag-to-multiple-objects' +# Body = @{ +# objects_ids = @($entMoRefs) +# tag_id = $tagId +# } +# } +# Invoke-vCisRest @sRest +# } +# elseif($Tag.Count -gt 1){ +# $tagIds | %{ +# $sRest = @{ +# Method = 'Post' +# Request = 'com/vmware/cis/tagging/tagassociation/id:$($tagId)?~action=attach-tag-to-multiple-objects' +# Body = @{ +# objects_ids = @($entMoRefs) +# tag_id = $_ +# } +# } +# Invoke-vCisRest @sRest +# } +# } +# } +# } +} + +function Remove-rCisTag{ + [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'High', DefaultParameterSetName='Name')] + param ( + [Parameter(Mandatory=$true, Position = 1, ValueFromPipeline = $true,ParameterSetName='Name')] + [PSObject[]]$Tag, + [Parameter(Mandatory=$true, Position = 1, ValueFromPipelineByPropertyName = $true,ParameterSetName='Id')] + [String[]]$Id + ) + + Process + { + if($PSCmdlet.ParameterSetName -eq 'Name'){ + foreach($tagObj in $Tag){ + if($tagObj -is [string]){ + $tagObj = Get-rCisTag -Name $tagObj + } + $sRest = @{ + Method = 'Delete' + Request = "com/vmware/cis/tagging/tag/id:$($tagObj.Id)" + } + Invoke-vCisRest @sRest + } + } + else{ + foreach($tagId in $Id){ + $sRest = @{ + Method = 'Delete' + Request = "com/vmware/cis/tagging/tag/id:$($tagId)" + } + Invoke-vCisRest @sRest + } + } + } +} + +function Remove-rCisTagCategory{ + [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'High', DefaultParameterSetName='Name')] + param ( + [Parameter(Mandatory=$true,Position = 1, ValueFromPipeline = $true,ParameterSetName='Name')] + [PSObject[]]$Category, + [Parameter(Mandatory=$true,Position = 1, ValueFromPipelineByPropertyName = $true,ParameterSetName='Id')] + [String[]]$Id + ) + + Process + { + if($PSCmdlet.ParameterSetName -eq 'Name'){ + foreach($catObj in $Category){ + if($catObj -is [string]){ + $catObj = Get-rCisTagCategory -Name $catObj + } + $sRest = @{ + Method = 'Delete' + Request = "com/vmware/cis/tagging/category/id:$($catObj.Id)" + } + Invoke-vCisRest @sRest + } + } + else{ + foreach($catId in $Id){ + $sRest = @{ + Method = 'Delete' + Request = "com/vmware/cis/tagging/category/id:$($catId)" + } + Invoke-vCisRest @sRest + } + } + } +} + +function Remove-rCisTagAssignment{ + [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'High',DefaultParameterSetName='Assignment')] + param ( + [Parameter(Mandatory=$true, Position = 1, ValueFromPipeline = $true,ParameterSetName='Assignment')] + [PSObject[]]$TagAssignment, + [Parameter(Mandatory=$true,Position = 1, ValueFromPipeline = $true,ParameterSetName='Name')] + [string[]]$Tag, + [Parameter(Position = 2, ParameterSetName='Name')] + [string[]]$Category, + [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName = $true,ParameterSetName='Id')] + [string[]]$TagId, + [Parameter(ParameterSetName='Name')] + [Parameter(ParameterSetName='Id')] + [PSObject[]]$Entity + ) + + Process + { + + switch ($PSCmdlet.ParameterSetName){ + 'Name' { + $TagAssignment = Get-rCisTagAssignment -Entity $Entity -Tag $Tag -Category $Category + } + 'Id' { + $tags = Get-rCisTag -Id $TagId + $TagAssignment = Get-rCisTagAssignment -Tag $tags.Name -Entity $Entity + } + } + if($TagAssignment){ + $entMoRefs = @(Get-Inventory -Name $TagAssignment.Entity -ErrorAction SilentlyContinue | %{ + New-Object PSObject -Property @{ + type = $_.ExtensionData.MoRef.Type + id = $_.ExtensionData.MoRef.Value + } + }) + $tagIds = @((Get-rCisTag -Name $TagAssignment.Tag).Id) + } + + foreach($entMoRef in $entMoRefs){ + foreach($tId in $tagIds){ + $sRest = @{ + Method = 'Post' + Request = "com/vmware/cis/tagging/tag-association/id:$($tId)?~action=detach" + Body = @{ + object_id = $entMoRef + } + } + Invoke-vCisRest @sRest + } + } + } +} + +function Set-rCisTag{ + [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory=$true, Position = 1, ValueFromPipeline = $true)] + [PSObject[]]$Tag, + [Parameter(Position = 2)] + [string]$Name, + [Parameter(Position = 3)] + [string]$Description + ) + + Process + { + foreach($tagObj in $Tag){ + if($tagObj -is [string]){ + $tagObj = Get-rCisTag -Name $tagObj + } + $sRest = @{ + Method = 'Patch' + Request = "com/vmware/cis/tagging/tag/id:$($tagObj.Id)" + Body = @{ + update_spec = @{ + name = $Name + description = $Description + } + } + } + Invoke-vCisRest @sRest + } + } +} + +function Set-rCisTagCategory{ + [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory=$true, Position = 1, ValueFromPipeline = $true)] + [PSObject[]]$Category, + [Parameter(Position = 2)] + [string]$Name, + [Parameter(Position = 3)] + [ValidateSet('Single','Multiple')] + [string]$Cardinality, # Only SINGLE to MULTIPLE +# [string[]]$AddEntityType, # Does not work + [string]$Description + ) + + Process + { + foreach($catObj in $Category){ + if($catObj -is [string]){ + $catObj = Get-rCisTagCategory -Name $catObj + } + $sRest = @{ + Method = 'Patch' + Request = "com/vmware/cis/tagging/category/id:$($catObj.Id)" + Body = @{ + update_spec = @{ + } + } + } + if($Name){ + $sRest.Body.update_spec.Add('name',$Name) + } + if($Description){ + $sRest.Body.update_spec.Add('description',$Description) + } + if($Cardinality -and $catObj.Cardinality -eq 'SINGLE'){ + $sRest.Body.update_spec.Add('cardinality',$Cardinality.ToUpper()) + } + if($Name -or $Description -or $Cardinality){ + Invoke-vCisRest @sRest + } + } + } +} diff --git a/Modules/vCenter.Alarms/New-vCenterAlarms.ps1 b/Modules/vCenter.Alarms/New-vCenterAlarms.ps1 new file mode 100644 index 0000000..e3ba5c8 --- /dev/null +++ b/Modules/vCenter.Alarms/New-vCenterAlarms.ps1 @@ -0,0 +1,79 @@ +<# + + =========================================================================== + Created by: Jason Robinson + Created on: 05/2017 + Twitter: @jrob24 + Filename: New-vCenterAlarms.ps1 + =========================================================================== + .DESCRIPTION + Examples of creating alarms using vCenter.Alarm module +#> + +Import-Module -Name vCenter.Alarms + +Write-Verbose -Message "Example 1 : Creating new Host CPU Usage alarm (Metric based alarm)" +Write-Verbose -Message "Finding the metric id for 'cpu.usage.average'" +$MetricId = (Get-MetricId -MetricGroup CPU | Where-Object -FilterScript { $_.Name -eq 'cpu.usage.average' }).Key +Write-Verbose -Message "Creating an alarm trigger for cpu.usage.average of 90% for 15mins (Warning) & 95% for 10mins (Alert) on the HostSystem object type" +$Trigger = New-AlarmTrigger -MetricId $MetricId -MetricOperator isAbove -ObjectType HostSystem -Yellow 90 -YellowInterval 15 -Red 95 -RedInterval 10 +Write-Verbose -Message "Creates a new alarm called 'Host CPU Usage' at the root level of vCenter" +New-AlarmDefinition -Name "Host CPU Usage" -Description "Alarm on 95%" -Entity Datacenters -Trigger $Trigger -ActionRepeatMinutes 10 +Write-Verbose -Message "Configures the alarm to send snmp traps" +Get-AlarmDefinition -Name "Host CPU Usage" | vSphere.Alarms\New-AlarmAction -Snmp -GreenToYellow Once -YellowToRed Repeat + +Write-Verbose -Message "Example 2 : Creating new HA Disabled alarm (Event based alarm)" +Write-Verbose -Message "Finding the event type for 'HA disabled for cluster'" +$EventType = (Get-EventId | Where-Object -FilterScript { $_.Description -match 'HA disabled for cluster' }).EventType +Write-Verbose -Message "Creating an alarm trigger for 'DasDisabledEvent' on the ClusterComputeResource object type" +$Trigger = New-AlarmTrigger -EventType $EventType -Status Red -ObjectType ClusterComputeResource +Write-Verbose -Message "Creates a new alarm called 'HA Disabled' at the root level of vCenter" +New-AlarmDefinition -Name "HA Disabled" -Description "Alarm on HA" -Entity Datacenters -Trigger $Trigger -ActionRepeatMinutes 30 +Write-Verbose -Message "Configures the alarm to send an email every 30mins" +$EmailParams = @{ + Email = $true + To = 'helpdesk@company.com' + Subject = 'HA Disabled' +} +Get-AlarmDefinition -Name "HA Disabled" | vCenter.Alarms\New-AlarmAction @EmailParams -YellowToRed Repeat + +Write-Verbose -Message "Example 3 : Creating new Host Connection State alarm (State based alarm)" +Write-Verbose -Message "Creating an alarm trigger for StateType of 'runtime.connectionState' on the HostSystem object type" +$Trigger = New-AlarmTrigger -StateType runtime.connectionState -StateOperator isEqual -YellowStateCondition disconnected -RedStateCondition notResponding -ObjectType HostSystem +Write-Verbose -Message "Creates a new alarm called 'Host Connection State' at the root level of vCenter" +New-AlarmDefinition -Name "Host Connection State" -Description "Connection State" -Entity Datacenters -Trigger $Trigger +Write-Verbose -Message "Configures the alarm to send an email once" +$EmailParams = @{ + Email = $true + To = 'helpdesk@company.com' + Subject = 'Host Connection Lost' +} +Get-AlarmDefinition -Name "Host Connection State" | vCenter.Alarms\New-AlarmAction @EmailParams -YellowToRed Once + +Write-Verbose -Message "Example 4 : Creating new Lost Storage Connectivity (Event based alarm)" +Write-Verbose -Message "Find the event type for 'Lost Storage Connectivity'" +Get-EventId | Where-Object -FilterScript { $_.Description -match 'Lost Storage Connectivity' } +Write-Verbose -Message "Two results returned, we want esx not vprob" + <# + EventType : EventEx + EventTypeId : esx.problem.storage.connectivity.lost + Category : error + Description : Lost Storage Connectivity + FullFormat : Lost connectivity to storage device { 1 }. Path { 2 } is down. Affected datastores: { 3 }. + vCenter : vCenter01 + + EventType : EventEx + EventTypeId : vprob.storage.connectivity.lost + Category : error + Description : Lost Storage Connectivity + FullFormat : Lost connectivity to storage device { 1 }. Path { 2 } is down. Affected datastores: { 3 }. + vCenter : vCenter01 + #> +Write-Verbose -Message "Since the event type is EventEx, we need both the EventType & EventTypeId to create the trigger" +$EventType = Get-EventId | Where-Object -FilterScript { $_.EventTypeId -eq 'esx.problem.storage.connectivity.lost' } +Write-Verbose -Message "Creating an alarm trigger for 'DasDisabledEvent' on the ClusterComputeResource object type" +$Trigger = New-AlarmTrigger -EventType $EventType.EventType -EventTypeId $EventType.EventTypeId -Status Red -ObjectType HostSystem +Write-Verbose -Message "Creates a new alarm called 'Lost Storage Connectivity' at the root level of vCenter" +New-AlarmDefinition -Name "Lost Storage Connectivity" -Description "Lost Storage" -Entity Datacenters -Trigger $Trigger -ActionRepeatMinutes 5 +Write-Verbose -Message "Configures the alarm to send an snmp every 5mins" +Get-AlarmDefinition -Name "Lost Storage Connectivity" | vCenter.Alarms\New-AlarmAction -Snmp -YellowToRed Repeat \ No newline at end of file diff --git a/Modules/vCenter.Alarms/vCenter.Alarms.psm1 b/Modules/vCenter.Alarms/vCenter.Alarms.psm1 new file mode 100644 index 0000000..5f13f3f --- /dev/null +++ b/Modules/vCenter.Alarms/vCenter.Alarms.psm1 @@ -0,0 +1,736 @@ +<# + =========================================================================== + Created by: Jason Robinson + Created on: 05/2017 + Twitter: @jrob24 + =========================================================================== + .DESCRIPTION + PowerShell Module to help with creation of vCenter Alarms + .NOTES + See New-vCenterAlarms.ps1 for examples of alarm creation + + * Tested against PowerShell 5.0 + * Tested against PowerCLI 6.5.1 build 5377412 + * Tested against vCenter 6.0 + * Tested against ESXi 5.5/6.0 +#> + +function New-AlarmDefinition { +<# + .SYNOPSIS + This cmdlet creates a new alarm defintion on the specified entity in vCenter. + .DESCRIPTION + This cmdlet creates a new alarm defintion on the specified entity in vCenter. + An alarm trigger is required in order to create a new alarm definition. + They can be created by using the New-AlarmTrigger cmdlet. + + After the alarm definition is created, if alarm actions are required use + the cmdlet New-AlarmAction to create actions for the alarm. + .PARAMETER Name + Specifies the name of the alarm you want to create. + .PARAMETER Description + Specifies the description for the alarm. + .PARAMETER Entity + Specifies where to create the alarm. To create the alarm at the root + level of vCenter use the entity 'Datacenters', otherwise specify any + object name. + .PARAMETER Trigger + Specifies the alarm event, state, or metric trigger(s). The alarm + trigger(s) are created with the New-AlarmTrigger cmdlet. For more + information about triggers, run Get-Help New-AlarmTrigger. + .PARAMETER Enabled + Specifies if the alarm is enabled when it is created. If unset, the + default value is true. + .PARAMETER ActionRepeatMinutes + Specifies the frequency how often the actions should repeat when an alarm + does not change state. + .PARAMETER ReportingFrequency + Specifies how often the alarm is triggered, measured in minutes. A zero + value means the alarm is allowed to trigger as often as possible. A + nonzero value means that any subsequent triggers are suppressed for a + period of minutes following a reported trigger. + + If unset, the default value is 0. Allowed range is 0 - 60. + .PARAMETER ToleranceRange + Specifies the tolerance range for the metric triggers, measure in + percentage. A zero value means that the alarm triggers whenever the metric + value is above or below the specified value. A nonzero means that the + alarm triggers only after reaching a certain percentage above or below + the nominal trigger value. + + If unset, the default value is 0. Allowed range is 0 - 100. + .PARAMETER Server + Specifies the vCenter Server system on which you want to run the cmdlet. + If no value is passed to this parameter, the command runs on the default + server, $DefaultVIServer. For more information about default servers, + see the description of Connect-VIServer. + .OUTPUTS + VMware.Vim.ManagedObjectReference + .NOTES + This cmdlet requires a connection to vCenter to create the alarm action. + .LINKS + http://pubs.vmware.com/vsphere-6-0/topic/com.vmware.wssdk.apiref.doc/vim.alarm.AlarmSpec.html + .EXAMPLE + PS C:\> $trigger = New-AlarmTrigger -StateType runtime.connectionState -StateOperator isEqual -YellowStateCondition disconnected -RedStateCondition notResponding -ObjectType HostSystem + PS C:\> New-AlarmDefinition -Name 'Host Connection' -Description 'Host Connection State Alarm -Entity Datacenters -Trigger $trigger -ActionRepeatMinutes 10 + + Type Value + ---- ----- + Alarm alarm-1801 + + This will create a host connection state alarm trigger and store it in + the variable $trigger. Then it will create a new alarm 'Host Connection' + on the root level of vCenter and set the action to repeat every 10 mins. +#> + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [Alias('AlarmName')] + [string]$Name, + + [string]$Description, + + [Parameter(Mandatory = $true)] + [string]$Entity, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [VMware.Vim.AlarmExpression[]]$Trigger, + + [boolean]$Enabled = $true, + + [ValidateRange(0, 60)] + [int32]$ActionRepeatMinutes, + + [ValidateRange(0, 60)] + [int32]$ReportingFrequency = 0, + + [ValidateRange(0, 100)] + [int32]$ToleranceRange = 0, + + [string]$Server + ) + BEGIN { + Write-Verbose -Message "Adding parameters with default values to PSBoundParameters" + foreach ($Key in $MyInvocation.MyCommand.Parameters.Keys) { + $Value = Get-Variable $Key -ValueOnly -ErrorAction SilentlyContinue + if ($Value -and !$PSBoundParameters.ContainsKey($Key)) { + $PSBoundParameters[$Key] = $Value + } + } + } + PROCESS { + try { + if ($PSBoundParameters.ContainsKey('Server')) { + $Object = Get-Inventory -Name $PSBoundParameters['Entity'] -ErrorAction Stop -Server $PSBoundParameters['Server'] + $AlarmMgr = Get-View AlarmManager -ErrorAction Stop -Server $PSBoundParameters['Server'] + } else { + $Object = Get-Inventory -Name $PSBoundParameters['Entity'] -ErrorAction Stop -Server $global:DefaultVIServer + $AlarmMgr = Get-View AlarmManager -ErrorAction Stop -Server $global:DefaultVIServer + } + + if ($PSCmdlet.ShouldProcess($global:DefaultVIServer, "Create alarm $($PSBoundParameters['Name'])")) { + $Alarm = New-Object -TypeName VMware.Vim.AlarmSpec + $Alarm.Name = $PSBoundParameters['Name'] + $Alarm.Description = $PSBoundParameters['Description'] + $Alarm.Enabled = $PSBoundParameters['Enabled'] + $Alarm.Expression = New-Object -TypeName VMware.Vim.OrAlarmExpression + $Alarm.Expression.Expression += $PSBoundParameters['Trigger'] + $Alarm.Setting = New-Object -TypeName VMware.Vim.AlarmSetting + $Alarm.Setting.ReportingFrequency = $PSBoundParameters['ReportingFrequency'] * 60 + $Alarm.Setting.ToleranceRange = $PSBoundParameters['ToleranceRange'] * 100 + $Alarm.ActionFrequency = $PSBoundParameters['ActionRepeatMinutes'] * 60 + $AlarmMgr.CreateAlarm($Object.Id, $Alarm) + } + } catch { + $PSCmdlet.ThrowTerminatingError($_) + } + } +} #End of New-AlarmDefinition function + +function New-AlarmAction { +<# + .SYNOPSIS + This cmdlet creates an alarm action on the specified alarm definition. + .DESCRIPTION + This cmdlet creates an alarm action on the specified alarm definition. + This cmdlet differs from the VMware PowerCLI New-AlarmAction cmdlet as it + will create the transitions of the alarm state. It requires an alarm + action and at least one transition to be specified. + + The transition indicates when the action executes and if it repeats. + There are only four acceptable transitions: green to yellow, yellow to + red, red to yellow, and yellow to green. At least one pair must be + specified or the results will be an invalid. + + If an alarm action already exists on the alarm definition, it will be + overwritten if the same alarm action is specified. For example if the + alarm definition already has an alarm action of Snmp on the transition + of green to yellow and the cmdlet is used to create a new action of + Snmp on the transition of yellow to red, it will overwrite the existing + action and transition. The end result will be one Snmp action on the + transition of yellow to red. If you want the old to transition to remain + both should be specified during the usage of the cmdlet. + .PARAMETER AlarmDefinition + Specifies the alarm definition for which you want to configure actions. + The alarm definition can be retreived by using the Get-AlarmDefinition + cmdlet. + .PARAMETER Snmp + Indicates that a SNMP message is sent when the alarm is activated. + .PARAMETER Email + Indicates that when the alarm is activated, the system sends an email + message to the specified address. Use the Subject, To, CC, and Body + parameters to customize the alarm message. + .PARAMETER To + Specifies the email address to which you want to send a message. + .PARAMETER Cc + Specifies the email address you want to add to the CC field of the email + message. + .PARAMETER Subject + Specifies a subject for the email address message you want to send. + .PARAMETER Body + Specifies the text of the email message. + .PARAMETER GreenToYellow + Specifies the alarm action for the green to yellow transition. Allowed + values are 'Once' and 'Repeat'. If parameter is not set transition will + remain unset. + .PARAMETER YellowToRed + Specifies the alarm action for the yellow to red transition. Allowed + values are 'Once' and 'Repeat'. If parameter is not set transition will + remain unset. + .PARAMETER RedToYellow + Specifies the alarm action for the red to yellow transition. Allowed + values are 'Once' and 'Repeat'. If parameter is not set transition will + remain unset. + .PARAMETER YellowToGreen + Specifies the alarm action for the yellow to green transition. Allowed + values are 'Once' and 'Repeat'. If parameter is not set transition will + remain unset. + .NOTES + This cmdlet requires a connection to vCenter to create the alarm action. + + When using this cmdlet specify the Module-Qualified cmdlet name to avoid + using the New-AlarmAction cmdlet with VMware PowerCLI. + .EXAMPLE + PS C:\> vCenter.Alarms\New-AlarmAction -AlarmDefinition (Get-AlarmDefintion "Host CPU Usage") -Snmp -YellowToRed Repeat + + This will create an Snmp alarm action on the "Host CPU Usage" alarm + transition of yellow to red. The alarm action will also repeat, as per + the action frequency defined on the alarm. + .EXAMPLE + PS C:\> Get-AlarmDefintion "Cluster HA Status" | vCenter.Alarms\New-AlarmAction -Email -To helpdesk@company.com -GreenToYellow Once -YellowToRed Once + + This will create an Email alarm action on the "Cluster HA Status" alarm + transition of green to yellow and yellow to red. The alarm action will + send an email to helpdesk@company.com one time per transition. +#> + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')] + param ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [VMware.VimAutomation.ViCore.Types.V1.Alarm.AlarmDefinition]$AlarmDefinition, + + [Parameter(Mandatory = $true, ParameterSetName = 'Snmp')] + [switch]$Snmp, + + [Parameter(Mandatory = $true, ParameterSetName = 'Email')] + [switch]$Email, + + [Parameter(Mandatory = $true, ParameterSetName = 'Email')] + [string[]]$To, + + [Parameter(ParameterSetName = 'Email')] + [string[]]$Cc, + + [Parameter(ParameterSetName = 'Email')] + [string]$Subject, + + [Parameter(ParameterSetName = 'Email')] + [string]$Body, + + [ValidateSet('Once', 'Repeat')] + [string]$GreenToYellow, + + [ValidateSet('Once', 'Repeat')] + [string]$YellowToRed, + + [ValidateSet('Once', 'Repeat')] + [string]$RedToYellow, + + [ValidateSet('Once', 'Repeat')] + [string]$YellowToGreen + ) + + BEGIN { + } + PROCESS { + try { + $AlarmView = Get-View -Id $PSBoundParameters['AlarmDefinition'].Id -Server ($PSBoundParameters['AlarmDefinition'].Uid.Split('@:')[1]) + $Alarm = New-Object -TypeName VMware.Vim.AlarmSpec + $Alarm.Name = $AlarmView.Info.Name + $Alarm.Description = $AlarmView.Info.Description + $Alarm.Enabled = $AlarmView.Info.Enabled + $Alarm.ActionFrequency = $AlarmView.Info.ActionFrequency + $Alarm.Action = New-Object VMware.Vim.GroupAlarmAction + $Trigger = New-Object VMware.Vim.AlarmTriggeringAction + + Write-Verbose -Message "Defining alarm actions" + if ($PSCmdlet.ParameterSetName -eq 'Snmp') { + $Trigger.Action = New-Object -TypeName VMware.Vim.SendSNMPAction + } elseif ($PSCmdlet.ParameterSetName -eq 'Email') { + $Trigger.Action = New-Object -TypeName VMware.Vim.SendEmailAction + $Trigger.Action.ToList = $PSBoundParameters['To'].GetEnumerator() | ForEach-Object -Process { + "$_;" + } + if ($PSBoundParameters.ContainsKey('Cc')) { + $Trigger.Action.CcList = $PSBoundParameters['Cc'].GetEnumerator() | ForEach-Object -Process { + "$_;" + } + } else { + $Trigger.Action.CcList = $null + } + $Trigger.Action.Subject = $PSBoundParameters['Subject'] + $Trigger.Action.Body = $PSBoundParameters['Body'] + } + + Write-Verbose -Message "Defining alarm transitions" + if ($PSBoundParameters.ContainsKey('GreenToYellow')) { + $Trans1 = New-Object -TypeName VMware.Vim.AlarmTriggeringActionTransitionSpec + $Trans1.StartState = 'green' + $Trans1.FinalState = 'yellow' + if ($PSBoundParameters['GreenToYellow'] -eq 'Repeat') { + $Trans1.Repeats = $true + } + $Trigger.TransitionSpecs += $Trans1 + } + + if ($PSBoundParameters.ContainsKey('YellowToRed')) { + $Trans2 = New-Object -TypeName VMware.Vim.AlarmTriggeringActionTransitionSpec + $Trans2.StartState = 'yellow' + $Trans2.FinalState = 'red' + if ($PSBoundParameters['YellowToRed'] -eq 'Repeat') { + $Trans2.Repeats = $true + } else { + $Trans2.Repeats = $false + } + $Trigger.TransitionSpecs += $Trans2 + } + + if ($PSBoundParameters.ContainsKey('RedToYellow')) { + $Trans3 = New-Object -TypeName VMware.Vim.AlarmTriggeringActionTransitionSpec + $Trans3.StartState = 'red' + $Trans3.FinalState = 'yellow' + if ($PSBoundParameters['RedToYellow'] -eq 'Repeat') { + $Trans3.Repeats = $true + } else { + $Trans3.Repeats = $false + } + $Trigger.TransitionSpecs += $Trans3 + } + + if ($PSBoundParameters.ContainsKey('YellowToGreen')) { + $Trans4 = New-Object -TypeName VMware.Vim.AlarmTriggeringActionTransitionSpec + $Trans4.StartState = 'yellow' + $Trans4.FinalState = 'green' + if ($PSBoundParameters['YellowToGreen'] -eq 'Repeat') { + $Trans4.Repeats = $true + } else { + $Trans4.Repeats = $false + } + $Trigger.TransitionSpecs += $Trans4 + } + + $Alarm.Action.Action += $Trigger + $Alarm.Expression = New-Object -TypeName VMware.Vim.OrAlarmExpression + $Alarm.Expression.Expression += $AlarmView.Info.Expression.Expression + $Alarm.Setting += $AlarmView.Info.Setting + $AlarmView.ReconfigureAlarm($Alarm) + } catch { + $PSCmdlet.ThrowTerminatingError($_) + } + } +} #End of New-AlarmAction function + +function New-AlarmTrigger { +<# + .SYNOPSIS + This cmdlet creates a vCenter event, state, or metric alarm trigger. + .DESCRIPTION + This cmdlet creates a vCenter event, state, or metric alarm trigger. + The trigger is used with the New-AlarmDefinition cmdlet to create a new + alarm in vCenter. This cmdlet will only create one alarm trigger. If more + triggers are required store the triggers in an array. + .PARAMETER EventType + Specifies the type of the event to trigger on. The event types can be + discovered by using the Get-EventId cmdlet. If the the event type is + 'EventEx' or 'ExtendedEvent' the EventTypeId parameter is required. + .PARAMETER EventTypeId + Specifies the id of the event type. Only used when the event type is an + 'EventEx' or 'ExtendedEvent'. + .PARAMETER Status + Specifies the status of the event. Allowed values are green, yellow, or + red. + .PARAMETER StateType + Specifies the state type to trigger on. Allowed values are + runtime.powerstate (HostSystem), summary.quickStats.guestHeartbeatStatus + (VirtualMachine), or runtime.connectionState (VirtualMachine). + .PARAMETER StateOperator + Specifies the operator condition on the target state. Allowed values are + 'isEqual' or 'isUnequal'. + .PARAMETER YellowStateCondition + Specifies the yellow state condition. When creating a state alarm + trigger at least one condition must be specified for a valid trigger to + be created. If the parameter is not set, the yellow condition is unset. + .PARAMETER RedStateCondition + Specifies the red state condition. When creating a state alarm trigger + at least one condition must be specified for a valid trigger to be + created. If the parameter is not set, the red condition is unset. + .PARAMETER MetricId + Specifies the id of the metric to trigger on. The metric ids can be + discovered by using the Get-MetricId cmdlet. + .PARAMETER MetricOperator + Specifies the operator condition on the target metric. Allowed values + are 'isAbove' or 'isBelow'. + .PARAMETER Yellow + Specifies the threshold value that triggers a yellow status. Allowed + range is 1% - 100%. + .PARAMETER YellowInterval + Specifies the time interval in minutes for which the yellow condition + must be true before the yellow status is triggered. If unset, the yellow + status is triggered immediately when the yellow condition becomes true. + .PARAMETER Red + Specifies the threshold value that triggers a red status. Allowed range + is 1% - 100%. + .PARAMETER RedInterval + Specifies the time interval in minutes for which the red condition must + be true before the red status is triggered. If unset, the red status is + triggered immediately when the red condition becomes true. + .PARAMETER ObjectType + Specifies the type of object on which the event is logged, the object + type containing the state condition or the type of object containing the + metric. + + When creating a state alarm trigger the only acceptable values are + 'HostSystem' or 'VirtualMachine'. The supported state types for each object + are as follows: + VirtualMachine type: runtime.powerState or summary.quickStats.guestHeartbeatStatus + HostSystem type: runtime.connectionState + .OUTPUTS + (Event|State|Metric)AlarmExpression + .NOTES + This cmdlet requires the PowerCLI module to be imported. + .LINK + Event Alarm Trigger + http://pubs.vmware.com/vsphere-6-0/topic/com.vmware.wssdk.apiref.doc/vim.alarm.EventAlarmExpression.html + + State Alarm Trigger + http://pubs.vmware.com/vsphere-6-0/topic/com.vmware.wssdk.apiref.doc/vim.alarm.StateAlarmExpression.html + + Metric Alarm Trigger + http://pubs.vmware.com/vsphere-6-0/topic/com.vmware.wssdk.apiref.doc/vim.alarm.MetricAlarmExpression.html + .EXAMPLE + PS C:\> New-AlarmTrigger -EventType "DasDisabledEvent" -Status Red -ObjectType ClusterComputeResource + + Comparisons : + EventType : DasDisabledEvent + ObjectType : ClusterComputeResource + Status : red + + Creates an event trigger on 'DasDisabledEvent' (HA Disabled) with a + status on 'Red'. The object type is a ClusterComputerResource because + this event occurs at a cluster level. + .EXAMPLE + PS C:\> New-AlarmTrigger -MetricId (Get-MetricId | Where Name -EQ 'cpu.usage.average').Key -Operator isAbove -Yellow 90 -YellowInterval 30 -Red 98 -RedInterval 15 -ObjectType HostSytem + + Operator : isAbove + Type : HostSytem + Metric : VMware.Vim.PerfMetricId + Yellow : 9000 + YellowInterval : 30 + Red : 9800 + RedInterval : 15 + + Creates a trigger on the 'cpu.usage.average' metric where the warning + condition must be above 90% for 30mins and the alert condition must be + above 98% for 15mins. The object type is a HostSystem. + .EXAMPLE + PS C:\temp> New-AlarmTrigger -StateType runtime.connectionState -StateOperator isEqual -YellowStateCondition Disconnected -RedStateCondition notResponding -ObjectType HostSystem + + Operator : isEqual + Type : HostSystem + StatePath : runtime.connectionState + Yellow : Disconnected + Red : notResponding + + Creates a trigger on the 'runtime.connectionState' condition where the + warning condition is 'disconnected' and the alert condition is + 'notResponding'. The object type is a HostSystem. +#> + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')] + param ( + [Parameter(Mandatory = $true, ParameterSetName = 'Event')] + [string]$EventType, + + [Parameter(ParameterSetName = 'Event')] + [string]$EventTypeId, + + [Parameter(Mandatory = $true, ParameterSetName = 'Event')] + [ValidateSet('Green', 'Yellow', 'Red')] + [string]$Status, + + [Parameter(Mandatory = $true, ParameterSetName = 'State')] + [ValidateSet('runtime.powerState', 'summary.quickStats.guestHeartbeatStatus', 'runtime.connectionState')] + [string]$StateType, + + [Parameter(Mandatory = $true, ParameterSetName = 'State')] + [VMware.Vim.StateAlarmOperator]$StateOperator, + + [Parameter(ParameterSetName = 'State')] + [ValidateSet('disconnected', 'notResponding', 'connected', 'noHeartbeat', 'intermittentHeartbeat', 'poweredOn', 'poweredOff', 'suspended')] + [string]$YellowStateCondition, + + [Parameter(ParameterSetName = 'State')] + [ValidateSet('disconnected', 'notResponding', 'connected', 'noHeartbeat', 'intermittentHeartbeat', 'poweredOn', 'poweredOff', 'suspended')] + [string]$RedStateCondition, + + [Parameter(Mandatory = $true, ParameterSetName = 'Metric')] + [string]$MetricId, + + [Parameter(Mandatory = $true, ParameterSetName = 'Metric')] + [VMware.Vim.MetricAlarmOperator]$MetricOperator, + + [Parameter(ParameterSetName = 'Metric')] + [ValidateRange(1, 100)] + [int32]$Yellow, + + [Parameter(ParameterSetName = 'Metric')] + [ValidateRange(1, 90)] + [int32]$YellowInterval, + + [Parameter(ParameterSetName = 'Metric')] + [ValidateRange(1, 100)] + [int32]$Red, + + [Parameter(ParameterSetName = 'Metric')] + [ValidateRange(1, 90)] + [int32]$RedInterval, + + [Parameter(Mandatory = $true)] + [ValidateSet('ClusterComputeResource', 'Datacenter', 'Datastore', 'DistributedVirtualSwitch', 'HostSystem', 'Network', 'ResourcePool', 'VirtualMachine')] + [string]$ObjectType + ) + try { + if ($PSCmdlet.ShouldProcess("vCenter alarm", "Create $($PSCmdlet.ParameterSetName) trigger")) { + if ($PSCmdlet.ParameterSetName -eq 'Event') { + $Expression = New-Object -TypeName VMware.Vim.EventAlarmExpression + $Expression.EventType = $PSBoundParameters['EventType'] + if ($PSBoundParameters.ContainsKey('EventTypeId')) { + $Expression.EventTypeId = $PSBoundParameters['EventTypeId'] + } + $Expression.ObjectType = $PSBoundParameters['ObjectType'] + $Expression.Status = $PSBoundParameters['Status'] + $Expression + } elseif ($PSCmdlet.ParameterSetName -eq 'Metric') { + $Expression = New-Object -TypeName VMware.Vim.MetricAlarmExpression + $Expression.Metric = New-Object -TypeName VMware.Vim.PerfMetricId + $Expression.Metric.CounterId = $PSBoundParameters['MetricId'] + $Expression.Metric.Instance = "" + $Expression.Operator = $PSBoundParameters['MetricOperator'] + $Expression.Red = ($PSBoundParameters['Red'] * 100) + $Expression.RedInterval = ($PSBoundParameters['RedInterval'] * 60) + $Expression.Yellow = ($PSBoundParameters['Yellow'] * 100) + $Expression.YellowInterval = ($PSBoundParameters['YellowInterval'] * 60) + $Expression.Type = $PSBoundParameters['ObjectType'] + $Expression + } elseif ($PSCmdlet.ParameterSetName -eq 'State') { + $Expression = New-Object -TypeName VMware.Vim.StateAlarmExpression + $Expression.Operator = $PSBoundParameters['StateOperator'] + $Expression.Type = $PSBoundParameters['ObjectType'] + $Expression.StatePath = $PSBoundParameters['StateType'] + + if ($PSBoundParameters.ContainsKey('RedStateCondition')) { + if ($PSBoundParameters['RedStateCondition'] -eq 'intermittentHeartbeat') { + $Expression.Red = 'yellow' + } elseif ($PSBoundParameters['RedStateCondition'] -eq 'noHeartbeat') { + $Expression.Red = 'red' + } else { + $Expression.Red = $PSBoundParameters['RedStateCondition'] + } + } + + if ($PSBoundParameters.ContainsKey('YellowStateCondition')) { + if ($PSBoundParameters['YellowStateCondition'] -eq 'intermittentHeartbeat') { + $Expression.Yellow = 'yellow' + } elseif ($PSBoundParameters['YellowStateCondition'] -eq 'noHeartbeat') { + $Expression.Yellow = 'red' + } else { + $Expression.Yellow = $PSBoundParameters['YellowStateCondition'] + } + } + $Expression + } + } + } catch { + $PSCmdlet.ThrowTerminatingError($_) + } +} #End of New-AlarmTrigger function + +function Get-MetricId { +<# + .SYNOPSIS + This cmdlet collects all of the available metrics from vCenter. + .DESCRIPTION + This cmdlet collects all of the available metrics from vCenter. It will + provide the metric name, key, stats level, and summary of the metric. + The information can be used to identify the available metrics on vCenter + as well as gathering the metric key needed for configuring an alarm. + + The metric keys are unique across vCenters. If you are connected to + more than one vCenter metrics from each vCenter will be generated. A + vCenter property is available to help determine the correct metric key + on a given vCenter. This is extrememly useful when trying to create + a metric based vCenter alarm. + .PARAMETER MetricGroup + Specifies the name of the metric group you would like to see. Allowed + values are 'CPU', 'Mem', 'Disk', 'Net', and 'Datastore'. + .OUTPUTS + System.Management.Automation.PSCustomObject + .NOTES + This cmdlet requires a connection to vCenter to collect metric data. + .EXAMPLE + PS C:\> Get-MetricId -MetricGroup Mem + + Name : mem.usage.none + Key : 23 + Level : 4 + Summary : Memory usage as percentage of total configured or available memory + vCenter : vCenter01 + + Name : mem.usage.average + Key : 24 + Level : 1 + Summary : Memory usage as percentage of total configured or available memory + vCenter : vCenter01 + + Name : mem.usage.minimum + Key : 25 + Level : 4 + Summary : Memory usage as percentage of total configured or available memory + vCenter : vCenter01 + ..... + + Collects all of the available memory metrics on the connected vCenter. +#> + [CmdletBinding()] + param ( + [ValidateSet('CPU', 'Mem', 'Disk', 'Net', 'Datastore')] + [string]$MetricGroup + ) + + foreach ($Mgr in (Get-View PerformanceManager-PerfMgr)) { + $vCenter = $Mgr.Client.ServiceUrl.Split('/')[2] + if ($PSBoundParameters.ContainsKey('MetricGroup')) { + $Metrics += $Mgr.PerfCounter | Where-Object -FilterScript { + $_.GroupInfo.Key -eq $PSBoundParameters['MetricGroup'] + } + } else { + $Metrics += $Mgr.PerfCounter + } + + $Metrics | ForEach-Object -Process { + [pscustomobject] @{ + Name = $_.GroupInfo.Key + "." + $_.NameInfo.key + "." + $_.RollupType + Key = $_.Key + Level = $_.Level + Summary = $_.NameInfo.Summary + vCenter = $vCenter + } + } + } +} #End of Get-MetricId function + +function Get-EventId { +<# + .SYNOPSIS + This cmdlet collects all of the available events from vCenter. + .DESCRIPTION + This cmdlet collects all of the available events from vCenter. It will + provide the event type, event type id (if applicable), category, + description, and summary of the event. The information can be used to + identify the available events on vCenter as well as gathering the event + type and event type id (if applicable) required for configuring an alarm. + + If the event type is 'EventEx' or 'ExtendedEvent' both the event type + and event type id will be required to create a new event based vCenter + alarm. + + The event types can be unique across vCenters. If you are connected to + more than one vCenter events from each vCenter will be generated. A + vCenter property is available to help determine the correct event type + on a given vCenter. This is extrememly useful when trying to create + a event based vCenter alarm. + .PARAMETER Category + Specifies the name of the event category you would like to see. Allowed + values are 'info', 'warning', 'error', and 'user'. + .OUTPUTS + System.Management.Automation.PSCustomObject + .NOTES + This cmdlet requires a connection to vCenter to collect event data. + .EXAMPLE + PS C:\> Get-EventId -Category Error + + EventType : ExtendedEvent + EventTypeId : ad.event.ImportCertFailedEvent + Category : error + Description : Import certificate failure + FullFormat : Import certificate failed. + vCenter : vCenter01 + + EventType : ExtendedEvent + EventTypeId : ad.event.JoinDomainFailedEvent + Category : error + Description : Join domain failure + FullFormat : Join domain failed. + vCenter : vCenter01 + + EventType : ExtendedEvent + EventTypeId : ad.event.LeaveDomainFailedEvent + Category : error + Description : Leave domain failure + FullFormat : Leave domain failed. + vCenter : vCenter01 + ..... +#> + [CmdletBinding()] + param ( + [VMware.Vim.EventCategory]$Category + ) + + foreach ($Mgr in (Get-View EventManager)) { + $vCenter = $Mgr.Client.ServiceUrl.Split('/')[2] + if ($PSBoundParameters.ContainsKey('Category')) { + $Events += $Mgr.Description.EventInfo | Where-Object -FilterScript { + $_.Category -eq $PSBoundParameters['Category'] + } + } else { + $Events += $Mgr.Description.EventInfo + } + + $Events | ForEach-Object -Process { + $Hash = [ordered]@{} + $Hash.Add('EventType', $_.Key) + if ($_.Key -eq 'ExtendedEvent' -or $_.Key -eq 'EventEx') { + $Hash.Add('EventTypeId', $_.FullFormat.Split('|')[0]) + } + $Hash.Add('Category', $_.Category) + $Hash.Add('Description', $_.Description) + if ($Hash['EventType'] -eq 'ExtendedEvent' -or $Hash['EventType'] -eq 'EventEx') { + $Hash.Add('FullFormat', $_.FullFormat.Split('|')[1]) + } else { + $Hash.Add('FullFormat', $_.FullFormat) + } + $Hash.Add('vCenter', $vCenter) + New-Object -TypeName System.Management.Automation.PSObject -Property $Hash + } + } +} #End of Get-EventId function \ No newline at end of file diff --git a/Modules/vCenterManualMigration/vCenterManualMigration.psm1 b/Modules/vCenterManualMigration/vCenterManualMigration.psm1 new file mode 100644 index 0000000..a7750b9 --- /dev/null +++ b/Modules/vCenterManualMigration/vCenterManualMigration.psm1 @@ -0,0 +1,475 @@ +Function Export-DRSRules { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/21/2017 + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Export DRS Rules to JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .DESCRIPTION + Export DRS Rules to JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .EXAMPLE + Export-DRSRules -Path C:\Users\primp\Desktop\VMworld2017 -Cluster Windows-Cluster +#> + param( + [Parameter(Mandatory=$false)][String]$Path, + [Parameter(Mandatory=$true)][String]$Cluster + ) + + $rules = Get-Cluster -Name $Cluster | Get-DrsRule + + $results = @() + foreach ($rule in $rules) { + $vmNames = @() + $vmIds = $rule.VMIds + + # Reconstruct MoRef ID to VM Object to get Name + foreach ($vmId in $vmIds) { + $vm = New-Object VMware.Vim.ManagedObjectReference + $vm.Type = "VirtualMachine" + $vm.Value = ($vmId -replace "VirtualMachine-","") + $vmView = Get-View $vm + $vmNames += $vmView.name + } + + $rulesObject = [pscustomobject] @{ + Name = $rule.ExtensionData.Name; + Type = $rule.Type; #VMAffinity = 1, VMAntiAffinity = 0 + Enabled = $rule.Enabled; + Mandatory = $rule.ExtensionData.Mandatory + VM = $vmNames + } + $results+=$rulesObject + } + if($Path) { + $fullPath = $Path + "\DRSRules.json" + Write-Host -ForegroundColor Green "Exporting DRS Rules to $fullpath ..." + $results | ConvertTo-Json | Out-File $fullPath + } else { + $results | ConvertTo-Json + } +} + +Function Import-DRSRules { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/21/2017 + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Import DRS Rules from JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .DESCRIPTION + Import DRS Rules from JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .EXAMPLE + Import-DRSRules -Path C:\Users\primp\Desktop\VMworld2017 -Cluster Windows-Cluster +#> + param( + [Parameter(Mandatory=$true)][String]$Path, + [Parameter(Mandatory=$true)][String]$Cluster + ) + + Get-DrsRule -Cluster $cluster | Remove-DrsRule -Confirm:$false | Out-Null + + $DRSRulesFilename = "/DRSRules.json" + $fullPath = $Path + $DRSRulesFilename + $json = Get-Content -Raw $fullPath | ConvertFrom-Json + + foreach ($line in $json) { + $vmArr = @() + $vmNames = $line.vm + foreach ($vmName in $vmNames) { + $vmView = Get-VM -Name $vmName + $vmArr+=$vmView + } + New-DrsRule -Name $line.name -Enabled $line.Enabled -Cluster (Get-Cluster -Name $Cluster) -KeepTogether $line.Type -VM $vmArr + } +} + +Function Export-DRSClusterGroup { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/21/2017 + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Export DRS Cluster Group Rules to JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .DESCRIPTION + Export DRS Cluster Group Rules to JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .EXAMPLE + Export-DRSClusterGroup -Path C:\Users\primp\Desktop\VMworld2017 -Cluster Windows-Cluster +#> + param( + [Parameter(Mandatory=$false)][String]$Path, + [Parameter(Mandatory=$true)][String]$Cluster + ) + + $rules = Get-Cluster -Name $Cluster | Get-DrsClusterGroup + + $results = @() + foreach ($rule in $rules) { + $rulesObject = [pscustomobject] @{ + Name = $rule.ExtensionData.Name; + Type = $rule.GroupType; #VMType = 1, HostType = 0 + Member = $rule.Member + } + $results+=$rulesObject + } + if($Path) { + $fullPath = $Path + "\DRSClusterGroupRules.json" + Write-Host -ForegroundColor Green "Exporting DRS Cluster Group Rules to $fullpath ..." + $results | ConvertTo-Json | Out-File $fullPath + } else { + $results | ConvertTo-Json + } +} + +Function Import-DRSClusterClusterGroup { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/21/2017 + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Import DRS Cluster Group Rules from JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .DESCRIPTION + Import DRS Cluster Group Rules from JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .EXAMPLE + Import-DRSClusterClusterGroup -Path C:\Users\primp\Desktop\VMworld2017 -Cluster Windows-Cluster +#> + param( + [Parameter(Mandatory=$true)][String]$Path, + [Parameter(Mandatory=$true)][String]$Cluster + ) + + $DRSClusterGroupRulesFilename = "\DRSClusterGroupRules.json" + $fullPath = $Path + $DRSClusterGroupRulesFilename + $json = Get-Content -Raw $fullPath | ConvertFrom-Json + + foreach ($line in $json) { + $memberArr = @() + $members = $line.member + + # VMHost Group + if($line.Type -eq 0) { + foreach ($member in $members) { + $memberView = Get-VMhost -Name $member + $memberArr+=$memberView + } + New-DrsClusterGroup -Name $line.name -Cluster (Get-Cluster -Name $Cluster) -VMhost $memberArr + # VM Group + } else { + foreach ($member in $members) { + $memberView = Get-VM -Name $member + $memberArr+=$memberView + } + New-DrsClusterGroup -Name $line.name -Cluster (Get-Cluster -Name $Cluster) -VM $memberArr + } + } +} + +Function Export-Tag { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/21/2017 + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Export vSphere Tags and VM Assocations to JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .DESCRIPTION + Export vSphere Tags and VM Assocations to JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .EXAMPLE + Export-Tag -Path C:\Users\primp\Desktop\VMworld2017 +#> + param( + [Parameter(Mandatory=$false)][String]$Path + ) + + # Export Tag Categories + $tagCatagorys = Get-TagCategory + + $tagCatresults = @() + foreach ($tagCategory in $tagCatagorys) { + $tagCatObj = [pscustomobject] @{ + Name = $tagCategory.Name; + Cardinality = $tagCategory.Cardinality; + Description = $tagCategory.Description; + Type = $tagCategory.EntityType + } + $tagCatresults+=$tagCatObj + } + if($Path) { + $fullPath = $Path + "\AllTagCategory.json" + Write-Host -ForegroundColor Green "Exporting vSphere Tag Category to $fullpath ..." + $tagCatresults | ConvertTo-Json | Out-File $fullPath + } else { + $tagCatresults | ConvertTo-Json + } + + # Export Tags + $tags = Get-Tag + + $tagResults = @() + foreach ($tag in $tags) { + $tagObj = [pscustomobject] @{ + Name = $tag.Name; + Description = $tag.Description; + Category = $tag.Category.Name + } + $tagResults+=$tagObj + } + if($Path) { + $fullPath = $Path + "\AllTag.json" + Write-Host -ForegroundColor Green "Exporting vSphere Tag to $fullpath ..." + $tagResults | ConvertTo-Json | Out-File $fullPath + } else { + $tagResults | ConvertTo-Json + } + + # Export VM to Tag Mappings + $vms = Get-VM + + $vmResults = @() + foreach ($vm in $vms) { + $tagAssignments = $vm | Get-TagAssignment + $tags = @() + foreach ($tagAssignment in $tagAssignments) { + $tag = $tagAssignment.Tag + $tagName = $tag -split "/" + $tags+=$tagName + } + $vmObj = [pscustomobject] @{ + Name = $vm.name; + Tag = $tags + } + $vmResults+=$vmObj + } + if($Path) { + $fullPath = $Path + "\AllTagAssocations.json" + Write-Host -ForegroundColor Green "Exporting VM to vSphere Tag Assignment to $fullpath ..." + $vmResults | ConvertTo-Json | Out-File $fullPath + } else { + $vmResults | ConvertTo-Json + } +} + +Function Import-Tag { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/21/2017 + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Import vSphere Tags and VM Assocations from JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .DESCRIPTION + Import vSphere Tags and VM Assocations from JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .EXAMPLE + Import-Tag -Path C:\Users\primp\Desktop\VMworld2017 +#> + param( + [Parameter(Mandatory=$true)][String]$Path + ) + + $tagCatFilename = "\AllTagCategory.json" + $fullPath = $Path + $tagCatFilename + $tagCategoryJson = Get-Content -Raw $fullPath | ConvertFrom-Json + + $tagFilename = "\AllTag.json" + $fullPath = $Path + $tagFilename + $tagJson = Get-Content -Raw $fullPath | ConvertFrom-Json + + $vmTagFilename = "\AllTagAssocations.json" + $fullPath = $Path + $vmTagFilename + $vmTagJson = Get-Content -Raw $fullPath | ConvertFrom-Json + + # Re-Create Tag Category + foreach ($category in $tagCategoryJson) { + if($category.Cardinality -eq 0) { + $cardinality = "Single" + } else { + $cardinality = "Multiple" + } + New-TagCategory -Name $category.Name -Cardinality $cardinality -Description $category.Description -EntityType $category.Type + } + + # Re-Create Tags + foreach ($tag in $tagJson) { + New-Tag -Name $tag.Name -Description $tag.Description -Category (Get-TagCategory -Name $tag.Category) + } + + # Re-Create VM to Tag Mappings + foreach ($vmTag in $vmTagJson) { + $vm = Get-VM -Name $vmTag.name + $tags = $vmTag.Tag + foreach ($tag in $tags) { + New-TagAssignment -Entity $vm -Tag (Get-Tag -Name $tag) + } + } +} + +Function Export-VMFolder { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/21/2017 + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Export vSphere Folder to JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .DESCRIPTION + Export vSphere Folder to JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .EXAMPLE + Export-VMFolder -Path C:\Users\primp\Desktop\VMworld2017 +#> + param( + [Parameter(Mandatory=$false)][String]$Path + ) + $vms = Get-VM + + $vmFolderResults = @() + foreach ($vm in $vms) { + $vmFolderObj = [pscustomobject] @{ + Name = $vm.name; + Folder = $vm.Folder.Name; + } + $vmFolderResults+=$vmFolderObj + } + if($Path) { + $fullPath = $Path + "\AllVMFolder.json" + Write-Host -ForegroundColor Green "Exporting VM Folders to $fullpath ..." + $vmFolderResults | ConvertTo-Json | Out-File $fullPath + } else { + $vmFolderResults | ConvertTo-Json + } +} + +Function Import-VMFolder { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/21/2017 + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Import vSphere Folder from JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .DESCRIPTION + Import vSphere Folder from JSON file based on VMworld Demo https://youtu.be/MagjfbIL4kg + .EXAMPLE + Import-VMFolder -Path C:\Users\primp\Desktop\VMworld2017 +#> + param( + [Parameter(Mandatory=$true)][String]$Path + ) + + $vmFolderFilename = "\AllVMFolder.json" + $fullPath = $Path + $vmFolderFilename + $vmFolderJson = Get-Content -Raw $fullPath | ConvertFrom-Json + + # Root vm Folder + $rootVMFolder = Get-Folder -Type VM -Name vm + + $folders = $vmFolderJson | Select Folder | Sort-Object -Property Folder -Unique + foreach ($folder in $folders) { + $rootVMFolder | New-Folder -Name $folder.folder + } + + foreach ($vmFolder in $vmFolderJson) { + $vm = Get-VM -Name $vmFolder.Name + $folder = Get-Folder -Name $vmFolder.Folder + Move-VM -VM $vm -Destination $folder + } +} + +Function Export-VMStoragePolicy { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/21/2017 + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Export VM Storage Policies to JSON file + .DESCRIPTION + Export VM Storage Policies to JSON file + .EXAMPLE + Export-VMStoragePolicy -Path C:\Users\primp\Desktop\VMworld2017 +#> + param( + [Parameter(Mandatory=$false)][String]$Path + ) + + foreach ($policy in Get-SpbmStoragePolicy) { + $policyName = $policy.Name + if($Path) { + Write-Host -ForegroundColor Green "Exporting Policy $policyName to $Path\$policyName.xml ..." + $policy | Export-SpbmStoragePolicy -FilePath $Path\$policyName.xml -Force | Out-Null + } else { + $policy + } + } +} + +Function Import-VMStoragePolicy { +<# + .NOTES + =========================================================================== + Created by: William Lam + Date: 11/21/2017 + Blog: https://www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + + .SYNOPSIS + Import VM Storage Policies from JSON file + .DESCRIPTION + Import VM Storage Policies from JSON file + .EXAMPLE + Import-VMStoragePolicy -Path C:\Users\primp\Desktop\VMworld2017 +#> + param( + [Parameter(Mandatory=$false)][String]$Path + ) + + foreach ($file in Get-ChildItem -Path $Path -Filter *.xml) { + $policyName = $file.name + $policyName = $policyName.replace(".xml","") + if(Get-SpbmStoragePolicy -Name $policyName -ErrorAction SilentlyContinue) { + Continue + } else { + Write-Host "Importing Policy $policyname ..." + Import-SpbmStoragePolicy -FilePath $Path\$file -Name $policyName + } + } +} \ No newline at end of file diff --git a/Modules/vSphere_Hardening_Assess_VM_v1a.psm1 b/Modules/vSphere_Hardening_Assess_VM_v1a/vSphere_Hardening_Assess_VM_v1a.psm1 similarity index 100% rename from Modules/vSphere_Hardening_Assess_VM_v1a.psm1 rename to Modules/vSphere_Hardening_Assess_VM_v1a/vSphere_Hardening_Assess_VM_v1a.psm1 diff --git a/Pester/00 Test Connect-CISServer Connection to VC.Tests.ps1 b/Pester/00 Test Connect-CISServer Connection to VC.Tests.ps1 new file mode 100644 index 0000000..e9fb3c9 --- /dev/null +++ b/Pester/00 Test Connect-CISServer Connection to VC.Tests.ps1 @@ -0,0 +1,37 @@ +<# +Script name: Test Connect-CISServer to VC.Tests.ps1 +Created on: 04/20/2017 +Author: Alan Renouf, @alanrenouf +Description: The purpose of this pester test is to ensure the PowerCLI modules are imported and a connection can be made to a vCenter for the CIS Service +Dependencies: Pester Module +Example run: + +Invoke-Pester -Script @{ Path = '.\Test Connect-CISServer to VC.Tests.ps1'; Parameters = @{ VCNAME="VC01.local"; VCUSER="Administrator@vsphere.local"; VCPASS="Admin!23"} } + +#> + +$VCUSER = $Parameters.Get_Item("VCUSER") +$VCPASS = $Parameters.Get_Item("VCPASS") +$VCNAME = $Parameters.Get_Item("VCNAME") + +Describe "Checking PowerCLI Cmdlets available" { + $cmdletname = "Connect-CISServer" + It "Checking $cmdletname is available" { + $command = Get-Command $cmdletname + $command | Select Name, Version + $command.Name| Should Be $cmdletname + } +} + +Describe "Connect-CISServer Tests" { + + $connection = Connect-CISServer $VCName -User $VCUser -password $VCPass + It "Connection is active" { + $Global:DefaultCISServers[0].isconnected | Should Be $true + } + + It "Checking connected server name is $VCName" { + $Global:DefaultCISServers[0] | Select * + $Global:DefaultCISServers[0].name | Should Be $VCName + } +} \ No newline at end of file diff --git a/Pester/00 Test Connect-VIServer Connection to VC.Tests.ps1 b/Pester/00 Test Connect-VIServer Connection to VC.Tests.ps1 new file mode 100644 index 0000000..2428458 --- /dev/null +++ b/Pester/00 Test Connect-VIServer Connection to VC.Tests.ps1 @@ -0,0 +1,36 @@ +<# +Script name: Test Connection to VC.ps1 +Created on: 07/15/2016 +Author: Alan Renouf, @alanrenouf +Description: The purpose of this pester test is to ensure the PowerCLI modules are imported and a connection can be made to a vCenter +Dependencies: Pester Module +Example run: + +Invoke-Pester -Script @{ Path = '.\Test Connection to VC.Tests.ps1'; Parameters = @{ VCNAME="VC01.local"; VCUSER="Administrator@vsphere.local"; VCPASS="Admin!23"} } + +#> + +$VCUSER = $Parameters.Get_Item("VCUSER") +$VCPASS = $Parameters.Get_Item("VCPASS") +$VCNAME = $Parameters.Get_Item("VCNAME") + +Describe "Checking PowerCLI Cmdlets available" { + $cmdletname = "Connect-VIServer" + It "Checking $cmdletname is available" { + $command = Get-Command $cmdletname + $command | Select Name, Version + $command.Name| Should Be $cmdletname + } +} + +Describe "Connect-VIServer Tests" { + + $connection = Connect-VIServer $VCName -User $VCUser -password $VCPass + It "Connection is active" { + $Global:DefaultVIServer[0].isconnected | Should Be $true + } + + It "Checking connected server name is $VCName" { + $Global:DefaultVIServer[0].name | Should Be $VCName + } +} \ No newline at end of file diff --git a/Pester/Test Connection to VC.ps1 b/Pester/Test Connection to VC.ps1 deleted file mode 100644 index 006825a..0000000 --- a/Pester/Test Connection to VC.ps1 +++ /dev/null @@ -1,44 +0,0 @@ -<# -Script name: Test Connection to VC.ps1 -Created on: 07/15/2016 -Author: Alan Renouf, @alanrenouf -Description: The purpose of this pester test is to ensure the PowerCLI modules are imported and a connection and disconnection can be made to a vCenter -Dependencies: Pester Module -Example run: - -Invoke-Pester -Script @{ Path = '.\Test Connection to VC.ps1'; Parameters = @{ VCNAME="VC01.local"; VCUSER="Administrator@vsphere.local"; VCPASS="Admin!23"} } - -#> - -$VCUSER = $Parameters.Get_Item("VCUSER") -$VCPASS = $Parameters.Get_Item("VCPASS") -$VCNAME = $Parameters.Get_Item("VCNAME") - -Describe "PowerCLI Tests" { - It "Importing PowerCLI Modules" { - Get-Module VMware* | Foreach { - Write-Host "Importing Module $($_.name) Version $($_.Version)" - $_ | Import-Module - Get-Module $_ | Should Be $true - } - } -} - -Describe "Connect-VIServer Tests" { - - $connection = Connect-VIServer $VCName -User $VCUser -password $VCPass - It "Connection is active" { - $Global:DefaultVIServer[0].isconnected | Should Be $true - } - - It "Checking connected server name is $VCName" { - $Global:DefaultVIServer[0].name | Should Be $VCName - } -} - -Describe "Disconnect-VIServer Tests" { - It "Disconnect from $VCName" { - Disconnect-VIServer $VCName -confirm:$false - $Global:DefaultVIServer | Should Be $null - } -} \ No newline at end of file diff --git a/Pester/Test Get-CISService.Tests.ps1 b/Pester/Test Get-CISService.Tests.ps1 new file mode 100644 index 0000000..17d18b5 --- /dev/null +++ b/Pester/Test Get-CISService.Tests.ps1 @@ -0,0 +1,49 @@ +<# +Script name: Test Connect-CISService.Tests.ps1 +Created on: 04/20/2017 +Author: Alan Renouf, @alanrenouf +Description: The purpose of this pester test is to ensure the CIS Service cmdlet works correctly +Dependencies: Pester Module +Example run: + +Invoke-Pester -Script @{ Path = '.\Test Get-CISService.ps1' } + +#> + +Describe "Checking PowerCLI Cmdlets available" { + $cmdletname = "Get-CISService" + It "Checking $cmdletname is available" { + $command = Get-Command $cmdletname + $command | Select Name, Version + $command.Name| Should Be $cmdletname + } +} + +Describe "Get-CISService Tests for services" { + + It "Checking CIS connection is active" { + $Global:DefaultCISServers[0].isconnected | Should Be $true + } + + It "Checking Get-CISService returns services" { + Get-CISService | Should Be $true + } + + # Checking some known services which have a Get Method + $servicestocheck = "com.vmware.appliance.system.version", "com.vmware.appliance.health.system" + Foreach ($service in $servicestocheck) { + It "Checking $service get method returns data" { + Get-CisService -Name $service | Should Be $true + (Get-CisService -Name $service).get() | Should Be $true + } + } + + # Checking some known services which have a List Method + $servicestocheck = "com.vmware.vcenter.folder", "com.vmware.vcenter.vm" + Foreach ($service in $servicestocheck) { + It "Checking $service list method returns data" { + Get-CisService -Name $service | Should Be $true + (Get-CisService -Name $service).list() | Should Be $true + } + } +} \ No newline at end of file diff --git a/Pester/ZZ Test Disconnect-CISServer to VC.Tests.ps1 b/Pester/ZZ Test Disconnect-CISServer to VC.Tests.ps1 new file mode 100644 index 0000000..63f12e4 --- /dev/null +++ b/Pester/ZZ Test Disconnect-CISServer to VC.Tests.ps1 @@ -0,0 +1,20 @@ +<# +Script name: Test Disconnect-CISServer to VC.Tests.ps1 +Created on: 04/20/2017 +Author: Alan Renouf, @alanrenouf +Description: The purpose of this pester test is to ensure the Disconnect-CISServer cmdlet disconnects +Dependencies: Pester Module +Example run: + +Invoke-Pester -Script @{ Path = '.\Test Disconnect-CISServer to VC.Tests.ps1'; Parameters = @{ VCNAME="VC01.local" } } + +#> + +$VCNAME = $Parameters.Get_Item("VCNAME") + +Describe "Disconnect-CISServer Tests" { + It "Disconnect from $VCName" { + Disconnect-CISServer $VCName -confirm:$false + $Global:DefaultCISServers | Should Be $null + } +} \ No newline at end of file diff --git a/Pester/ZZ Test Disconnect-VIServer to VC.Tests.ps1 b/Pester/ZZ Test Disconnect-VIServer to VC.Tests.ps1 new file mode 100644 index 0000000..8a51fb9 --- /dev/null +++ b/Pester/ZZ Test Disconnect-VIServer to VC.Tests.ps1 @@ -0,0 +1,20 @@ +<# +Script name: Test Disconnect-VIServer to VC.ps1 +Created on: 04/20/2017 +Author: Alan Renouf, @alanrenouf +Description: The purpose of this pester test is to ensure the Disconnect-VIServer cmdlet disconnects +Dependencies: Pester Module +Example run: + +Invoke-Pester -Script @{ Path = '.\Test Disconnect-VISServer to VC.ps1'; Parameters = @{ VCNAME="VC01.local" } } + +#> + +$VCNAME = $Parameters.Get_Item("VCNAME") + +Describe "Disconnect-VIServer Tests" { + It "Disconnect from $VCName" { + Disconnect-VIServer $VCName -confirm:$false + $Global:DefaultVIServer | Should Be $null + } +} \ No newline at end of file diff --git a/README.md b/README.md index 7d2825c..a82900f 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,8 @@ * [Security](https://github.com/vmware/PowerCLI-Example-Scripts#security) * [Resource Maintenance](https://github.com/vmware/PowerCLI-Example-Scripts#resource-maintenance) * [Maintenance Ownership](https://github.com/vmware/PowerCLI-Example-Scripts#maintenance-ownership) - * [Filing issues](https://github.com/vmware/PowerCLI-Example-Scripts#filing-isssues) - * [Resolving issues](https://github.com/vmware/PowerCLI-Example-Scripts#resolving-issues) + * [Filing Issues](https://github.com/vmware/PowerCLI-Example-Scripts#filing-isssues) + * [Resolving Issues](https://github.com/vmware/PowerCLI-Example-Scripts#resolving-issues) * [Additional Resources](https://github.com/vmware/PowerCLI-Example-Scripts#additional-resources) * [Discussions](https://github.com/vmware/PowerCLI-Example-Scripts#discussions) * [VMware Sample Exchange](https://github.com/vmware/PowerCLI-Example-Scripts#vmware-sample-exchange) @@ -59,15 +59,15 @@ The repository has been provided to allow the community to share resources that 1. Browse to the appropriate section (example: Scripts) 2. Select the “Create new file” button 3. On the new page, enter a file name, enter the resource’s information -4. Within the “Commit new file” area, enter the title and description, then select “Create a new branch for this commit…” and enter a sensical branch name +4. Within the “Commit new file” area, enter the title and description, then select “Create a new branch for this commit…” and enter a sensible branch name 5. Click “Propose new file” 6. On the “Open a pull request” page, click “Create pull request” #### GitHub - Upload files Option 1. Browse to the appropriate section (example: Modules) 2. Select the “Upload files” button -3. On the new page, drag or choose the files to add -4. Within the “Commit changes” area, enter the title and description, then select “Create a new branch for this commit…” and enter a sensical branch name +3. On the new page, drag or choose the files to add +4. Within the “Commit changes” area, enter the title and description, then select “Create a new branch for this commit…” and enter a sensible branch name 5. Click “Propose new file” 6. On the “Open a pull request” page, click “Create pull request” @@ -88,7 +88,7 @@ The following information must be included with each submitted scripting resourc #### Note Placement Examples: Script: Top few lines Module: Module manifest - + #### Required Script Note Example: `<#` `Script name: script_name.ps1` @@ -126,7 +126,7 @@ The following information should be included when possible. Inclusion of informa This section describes guidelines put in place to maintain a standard of quality while also promoting broader contribution. ### General Best Practices ### Resource Naming -* Give the resource a name that is indicitive of the actions and/or results of its running +* Give the resource a name that is indicative of the actions and/or results of its running ### Fault Handling * Read and apply the following basic fault handling where applicable: Microsoft’s Hey, Scripting Guy! Blog: https://blogs.technet.microsoft.com/heyscriptingguy/2014/07/09/handling-errors-the-powershell-way/ @@ -138,7 +138,7 @@ This section describes guidelines put in place to maintain a standard of quality * Avoid changing any global variables ### Help Information -* All resources should have inline documentation. +* All resources should have inline documentation. ### Scripts * The script should be easy to read and understand @@ -149,27 +149,27 @@ This section describes guidelines put in place to maintain a standard of quality * Use only standard verbs ### Security -* Usage of PowerShell’s strict mode is preferred, but not required. +* Usage of PowerShell’s strict mode is preferred, but not required. * Remove any information related to one’s own environment (examples: Passwords, DNS/IP Addresses, custom user credentials, etc) ## Resource Maintenance ### Maintenance Ownership -Ownership of any and all submitted resources are maintained by the submitter. This ownership also includes maintenance of any and all submitted resources. +Ownership of any and all submitted resources are maintained by the submitter. This ownership also includes maintenance of any and all submitted resources. ### Filing Issues -Any bugs or other issues should be filed within GitHub by way of the repository’s Issue Tracker. -### Resolving Issues -Any community member can resolve issues within the repository, however only the owner or a board member can approve the update. Once approved, assuming the resolution involves a pull request, only a board member will be able to merge and close the request. +Any bugs or other issues should be filed within GitHub by way of the repository’s Issue Tracker. +### Resolving Issues +Any community member can resolve issues within the repository, however only the owner or a board member can approve the update. Once approved, assuming the resolution involves a pull request, only a board member will be able to merge and close the request. ## Additional Resources ### Discussions Join in on the discussion within the VMware Code Slack team's PowerCLI channel: ### VMware Sample Exchange -It is highly recommened to add any and all submitted resources to the VMware Sample Exchange: - +It is highly recommended to add any and all submitted resources to the VMware Sample Exchange: + Sample Exchange can be allowed to access your GitHub resources, by way of a linking process, where they can be indexed and searched by the community. There are VMware social media accounts which will advertise resources posted to the site and there's no additional accounts needed, as the VMware Sample Exchange uses MyVMware credentials. ## VMWARE TECHNOLOGY PREVIEW LICENSE AGREEMENT -The VMware Technnology Preview License Agreement: +The VMware Technology Preview License Agreement: # Repository Administrator Resources ## Table of Contents @@ -180,7 +180,7 @@ The VMware Technnology Preview License Agreement: + +$esxhost="HOSTNAME" +$vswitch="vSwitch0" +$vlanlist=10,20,30,40,50 +Foreach ($vlan in $vlanlist) { + $portgroupname="VLAN " + $vlan + Get-VMHost $esxhost | Get-VirtualSwitch -name $vswitch | New-VirtualPortGroup -Name $portgroupname -VLanId $vlan +} +#The End diff --git a/Scripts/Get-BasicVMCapacityReport b/Scripts/Get-BasicVMCapacityReport new file mode 100644 index 0000000..edbe9c7 --- /dev/null +++ b/Scripts/Get-BasicVMCapacityReport @@ -0,0 +1,30 @@ + +$myCol = @() +$start = (Get-Date).AddDays(-30) +$finish = Get-Date +$cluster= "Demo" + +$objServers = Get-Cluster $cluster | Get-VM +foreach ($server in $objServers) { + if ($server.guest.osfullname -ne $NULL){ + if ($server.guest.osfullname.contains("Windows")){ + $stats = get-stat -Entity $server -Stat "cpu.usage.average","mem.usage.average" -Start $start -Finish $finish + + $ServerInfo = "" | Select-Object vName, OS, Mem, AvgMem, MaxMem, CPU, AvgCPU, MaxCPU, pDisk, Host + $ServerInfo.vName = $server.name + $ServerInfo.OS = $server.guest.osfullname + $ServerInfo.Host = $server.vmhost.name + $ServerInfo.Mem = $server.memoryGB + $ServerInfo.AvgMem = $("{0:N2}" -f ($stats | Where-Object {$_.MetricId -eq "mem.usage.average"} | Measure-Object -Property Value -Average).Average) + $ServerInfo.MaxMem = $("{0:N2}" -f ($stats | Where-Object {$_.MetricId -eq "mem.usage.average"} | Measure-Object -Property Value -Maximum).Maximum) + $ServerInfo.CPU = $server.numcpu + $ServerInfo.AvgCPU = $("{0:N2}" -f ($stats | Where-Object {$_.MetricId -eq "cpu.usage.average"} | Measure-Object -Property Value -Average).Average) + $ServerInfo.MaxCPU = $("{0:N2}" -f ($stats | Where-Object {$_.MetricId -eq "cpu.usage.average"} | Measure-Object -Property Value -Maximum).Maximum) + $ServerInfo.pDisk = [Math]::Round($server.ProvisionedSpaceGB,2) + + $mycol += $ServerInfo + } + } +} + +$myCol | Sort-Object vName | Export-Csv "VM_report.csv" -NoTypeInformation diff --git a/Scripts/Get-CIVMData.ps1 b/Scripts/Get-CIVMData.ps1 new file mode 100644 index 0000000..7215cfc --- /dev/null +++ b/Scripts/Get-CIVMData.ps1 @@ -0,0 +1,235 @@ +Function Get-CIVMData +{ +<# + .SYNOPSIS + Gathers information about a target CIVM + + .DESCRIPTION + This function gathers CIVM Name, Parent vApp (obj), Parent vApp Name, All network adapters + (including IP, NIC index, and network), and vCenter VMX path details returning the resulting + ordered list. + + .PARAMETER CIVM + The target vCloud VM from which information will be gathered + + .NOTES + Author: Brian Marsh + Version: 1.0 +#> + + [CmdletBinding()] + Param ( + [Parameter( + Position=0, + Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true) + ] + [VMware.VimAutomation.Cloud.Types.V1.CIVM] $CIVM + ) + BEGIN + { + + } + + PROCESS + { + $NewObj = [Ordered]@{} + $NewObj.GetCIVMData = @{} + $NewObj.GetCIVMData.Successful = $true + + # Get the vCenter VM from the vCloud VM object + $vm = $civm | Get-VM -Debug:$False -Verbose:$False + + Write-Verbose "Storing CIVM Name: $($CIVM.Name)/ Status: $($CIVM.Status)" + $NewObj.Name = $CIVM.Name + $NewObj.Status = $CIVM.Status + + Write-Verbose "Recording Reservations" + $NewObj.Reservations = @{} + $NewObj.Reservations.CPU = @{} + $NewObj.Reservations.Memory = @{} + + $NewObj.Reservations.CPU.Reservation = $vm.ExtensionData.ResourceConfig.CpuAllocation.Reservation + $NewObj.Reservations.CPU.Limit = $vm.ExtensionData.ResourceConfig.CpuAllocation.Limit + $NewObj.Reservations.Memory.Reservation = $vm.ExtensionData.ResourceConfig.MemoryAllocation.Reservation + $NewObj.Reservations.Memory.Limit = $vm.ExtensionData.ResourceConfig.MemoryAllocation.Limit + + # Get the UUid from the Id, split out the UUID and pass it along + # Sample Id: urn:vcloud:vm:d9ca710d-cdf2-44eb-a274-26e1dcfd01bb + Write-Verbose "Storing CIVM UUID: $(($CIVM.Id).Split(':')[3])" + $NewObj.Uuid = ($CIVM.Id).Split(':')[3] + + Write-Verbose "Gathering Network details" + $vAppNetworkAdapters = @() + $NetworkAdapters = Get-CINetworkAdapter -VM $civm -Debug:$False -Verbose:$False + + foreach ($networkAdapter in $networkAdapters) + { + # Remove any existing VMNIC variables + Remove-Variable -Name VMNic -ErrorAction SilentlyContinue + + $vAppNicInfo = [Ordered]@{} + $vAppNicInfo.NIC = ("NIC" + $networkAdapter.Index) + $vAppNicInfo.Index = $networkAdapter.Index + $vAppNicInfo.Connected = $networkAdapter.Connected + $vAppNicInfo.ExternalIP = $networkAdapter.IpAddress + $vAppNicInfo.InternalIP = $networkAdapter.ExternalIpAddress + $vAppNicInfo.MacAddress = $networkAdapter.MACAddress + + $vAppNicInfo.vAppNetwork = [Ordered]@{} + $vAppNicInfo.vAppNetwork.Name = $networkAdapter.VAppNetwork.Name + + <# + There is a chance that the vApp Network Name may not match a PortGroup which causes issues upon importing the VM after migration. + To fix this issue, we'll try to find get the PortGroup in this data gathering stage. If it is not found, we'll move on to attempted + remediation: + 1) Get the vCenter VM network adapter that corresponds to this vCloud Director VM network adapter (where MAC Addresses match) + 2) If the vCenter VM network adapter's network name doesn't match 'none' (indicating the VM is powered off) and the vCenter Network + name does not match the vCloud Director network name, set this target object's vAppNetwork Name to the vCenter PortGroup + 3) If the vCenter VM network adapter's network name is 'none' then this VM is probably powered off and the network information is + not defined in vCenter. In this case, we mark the get-data as unsuccessful, set an error message and return. + #> + try + { + $vm | Get-VMHost -Debug:$false -Verbose:$false | Get-VDSwitch -Debug:$false -Verbose:$false -ErrorAction Stop | ` + Get-VDPortgroup -name $networkAdapter.vAppNetwork.Name -Debug:$false -Verbose:$false -ErrorAction Stop | Out-Null + } + catch + { + Write-Debug "Portgroup not found by name $($networkAdapter.vAppNetwork.Name), Debug?" + Write-Verbose "Portgroup not found by name $($networkAdapter.vAppNetwork.Name), attempting fall back." + # Get VIVM network adapter where adapter mac matches vappnicinfo MacAddress + $VMNic = $vm | Get-NetworkAdapter -Debug:$false -Verbose:$false | Where-Object { $_.MacAddress -eq $vAppNicInfo.MacAddress } + + # If VMNic Network Name doesn't match 'none' and doesn't match the vAppNetworkName, set vAppNetwork name to VMNic Network name + If ( ($VMNic.NetworkName -notlike 'none') -and ($VMNic.NetworkName -ne $vAppNicInfo.vAppNetwork.Name)) + { + $vAppNicInfo.vAppNetwork.Name = $VMNic.NetworkName + } + else + { + Write-Debug "Tried to recover from missing network port group. Failed. Debug?" + $ErrorMessage = "VM [ $($CIVM.Name) ] has vAppNetwork connection that doesn't exist in vCenter [ $($vAppNicInfo.vAppNetwork.Name) ]" + $NewObj.GetCIVMData.Successful = $False + $NewObj.GetCIVMData.Error = $ErrorMessage + Write-Error $ErrorMessage + + #Return whatever object we have at this point + $NewObj + + Return + } + + } + + $vAppNetworkAdapters += $vAppNicInfo + } + + Write-Verbose "Checking for Duplicate name upon Import" + Try + { + $DupeVM = Get-VM -Name $NewObj.NewName -Debug:$false -Verbose:$false -ErrorAction Stop -ErrorVariable DupeVM + If ($DupeVM) + { + $NewObj.GetCIVMData.Successful = $False + $NewObj.GetCIVMData.Error = "VM with name $($NewObj.NewName) already exists in vCenter" + Write-Error "VM with name $($NewObj.NewName) already exists in vCenter" + + #Return whatever object we have at this point + $NewObj + + return + } + } + Catch + { + Write-Verbose "No Duplicate Name Found!" + } + + $NewObj.vAppNetworkAdapters = $vAppNetworkAdapters + + Write-Verbose "Setting VIVIM object, parent vApp details, and CIVM object" + try + { + $NewObj.VIVM = $vm + $NewObj.ToolsStatus = $vm.ExtensionData.Guest.ToolsStatus + $NewObj.ToolsRunningStatus = $vm.ExtensionData.Guest.ToolsRunningStatus + $NewObj.HasSnapshots = ($vm | Get-Snapshot -Debug:$false -Verbose:$false -ErrorAction Stop | Select-Object Name, Description,VMId) + $NewObj.NeedsConsolidation = $vm.ExtensionData.Runtime.ConsolidationNeeded + $NewObj.OldMoref = $vm.Id + $NewObj.VmPathName = $vm.ExtensionData.Config.Files.VmPathName + $NewObj.ParentVApp = $CIVM.VApp.Name + $NewObj.StorageReservation = ($vm |Get-DatastoreCluster -Debug:$false -Verbose:$false -ErrorAction Stop | Select-Object -ExpandProperty Name) + $NewObj.CIVMId = $CIVM.Id + } + catch + { + $NewObj.GetCIVMData.Successful = $False + $NewObj.GetCIVMData.Error = "VM [ $($CIVM.Name) ] something went wrong while gathering details: $_" + Write-Debug "VM [ $($CIVM.Name) ] something went wrong while gathering details: $_, Debug" + Write-Error "VM [ $($CIVM.Name) ] something went wrong while gathering details: $_. " + + #Return whatever object we have at this point + $NewObj + + Return + } + + # If ToolsStatus is not 'toolsOk' and status is not "PoweredOn", bomb out. We won't be able to power this VM off later. + If ($NewObj.ToolsRunningStatus -ne 'guestToolsRunning' -and $NewObj.status -eq "PoweredOn") + { + $NewObj.GetCIVMData.Successful = $False + $NewObj.GetCIVMData.Error = "VM [ $($CIVM.Name) ] tools are not running but the VM is powered On. Fix and try again." + Write-Debug "VM [ $($CIVM.Name) ] tools are not running but the VM is powered On, Debug" + Write-Error "VM [ $($CIVM.Name) ] tools are not running but the VM is powered On. " + + #Return whatever object we have at this point + $NewObj + + Return + } + + If ($NewObj.HasSnapshots) + { + $NewObj.GetCIVMData.Successful = $False + $NewObj.GetCIVMData.Error = "VM [ $($CIVM.Name) ] has snapshots. Remove before trying again." + Write-Debug "VM [ $($CIVM.Name) ] has snapshots. Remove before trying again, Debug" + Write-Error "VM [ $($CIVM.Name) ] has snapshots. Remove before trying again." + + #Return whatever object we have at this point + $NewObj + + Return + } + + Write-Verbose "Determining the VMX Path for this VM" + + # Get this VM's path on disk + $vmPathName = $vm.ExtensionData.Config.Files.VmPathName + + # Determine in which Datacenter this VM resides + $datacenter = $vm | get-Datacenter -Debug:$False -Verbose:$False | Select-Object -expand name + + # Split out the datastore from the path name + $datastore = $vmPathName.Split("]")[0].split("[")[1] + + # Split out the folder from the path name + $vmFolderPath = $vmPathName.Split("/")[0].split("]")[1].trim() + + # Re-combine into a valid folder path + $vmxPath = "vmstore:\$($datacenter)\$($datastore)\$vmFolderPath" + + Write-Verbose "VMXPath $vmxPath" + $NewObj.vmxPath = $vmxPath + + $NewObj + + } + + END + { + Write-Debug "About to exit Get-CIVMData, anything else?" + Write-Verbose "Exited Get-CIVMData" + } +} diff --git a/Scripts/Get-TotalDiskUsage.ps1 b/Scripts/Get-TotalDiskUsage.ps1 new file mode 100644 index 0000000..b14e7d5 --- /dev/null +++ b/Scripts/Get-TotalDiskUsage.ps1 @@ -0,0 +1,4 @@ +#Script returns total disk usage by all Powered On VMs in the environment in Gigabytes +#Author: Chris Bradshaw via https://isjw.uk/using-powercli-to-measure-vm-disk-space-usage/ + +[math]::Round(((get-vm | Where-object{$_.PowerState -eq "PoweredOn" }).UsedSpaceGB | measure-Object -Sum).Sum) diff --git a/Scripts/Get-TotalMemoryAllocation.ps1 b/Scripts/Get-TotalMemoryAllocation.ps1 new file mode 100644 index 0000000..6e6736d --- /dev/null +++ b/Scripts/Get-TotalMemoryAllocation.ps1 @@ -0,0 +1,6 @@ +#Script gets total memory allocation in GB of all powered on VMs in the environment +#Author: Chris Bradshaw via https://isjw.uk/powercli-snippet-total-memory-allocation/ + +[System.Math]::Round(((get-vm | + where-object{$_.PowerState -eq "PoweredOn" }).MemoryGB | + Measure-Object -Sum).Sum ,0) diff --git a/Scripts/Get-VMHostWWPN.ps1 b/Scripts/Get-VMHostWWPN.ps1 new file mode 100644 index 0000000..921b5bb --- /dev/null +++ b/Scripts/Get-VMHostWWPN.ps1 @@ -0,0 +1,17 @@ +function Get-VMHostWWPN { +<# +Script name: Get-VMHostWWPN.ps1 +Created on: 08/31/2017 +Author: Robin Haberstroh, @strohland +Description: This script returns the WWPN of the hosts FiberChannel HBA in a readable format that corresponds to what storage team expects +Dependencies: None known +#> + param( + [string]$cluster + ) + + Get-Cluster $cluster | get-vmhost | get-vmhosthba -type FibreChannel | + format-table VMHost, Device, @{ + n='WorldWidePortName';e={[convert]::ToString($_.PortWorldWideName, 16)} + } +} \ No newline at end of file diff --git a/Scripts/Get-VMNetworkPortId.ps1 b/Scripts/Get-VMNetworkPortId.ps1 new file mode 100644 index 0000000..eeb2e62 --- /dev/null +++ b/Scripts/Get-VMNetworkPortId.ps1 @@ -0,0 +1,56 @@ +<# +.SYNOPSIS + Finds the local ESXi network Port-ID where a VM is assigned +.DESCRIPTION + Reports back a VM's Port-ID according to the local ESXi host. This correlates to the Port-ID which is displayed via ESXTop +.NOTES + Author: Kyle Ruddy, @kmruddy, thatcouldbeaproblem.com +.PARAMETER vm + The name of the desired VM +.EXAMPLE + PS> .\Get-VMNetworkPortId.ps1 -vm vmname +.EXAMPLE + PS> Get-VM -Name vmname | .\Get-VMNetworkPortId.ps1 +#> +[CmdletBinding(SupportsShouldProcess=$True)] + param( + [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true)] + [Alias('Name')] + [String[]]$vm + ) + + Begin { + #Create an array to store output prior to return + $output = @() + + } + + Process { + #Loop through each of the input values + foreach ($v in $vm) { + #Validate the input is a valid VM + $vmobj = Get-VM -Name $v -erroraction silentlycontinue + if (!$vmobj) {Write-Verbose "No VM found by the name $v."} + else { + #Create a temporary object to store individual ouput + $tempout = "" | select VM,PortId + #Start an ESXCLI session with the host where the VM resides + $esxcli = Get-EsxCli -VMHost $vmobj.VMHost -v2 + #ESXCLI call to obtain information about the VM, specifically its WorldID + $vmNetInfo = $esxcli.network.vm.list.Invoke() | ?{$_.Name -eq $vmobj.Name} + #Create spec to poll the host for the network information of the VM + $portArgs = $esxcli.network.vm.port.list.CreateArgs() + $portArgs.worldid = $vmNetInfo.WorldID + #Output the values to the temporary object + $tempout.VM = $vmobj.Name + $tempout.PortId = $esxcli.network.vm.port.list.Invoke($portArgs).PortId + $output += $tempout + } + } + } + + End { + + return $output + + } diff --git a/Scripts/Get-VMToolsParts.ps1 b/Scripts/Get-VMToolsParts.ps1 new file mode 100644 index 0000000..c4a3d51 --- /dev/null +++ b/Scripts/Get-VMToolsParts.ps1 @@ -0,0 +1,11 @@ +$vms = Get-VM | where {$_.PowerState -eq "PoweredOn" -and $_.GuestId -match "Windows"} + +ForEach ($vm in $vms){ + Write-Host $vm + $namespace = "root\CIMV2" + $componentPattern = "hcmon|vmci|vmdebug|vmhgfs|VMMEMCTL|vmmouse|vmrawdsk|vmxnet|vmx_svga" + (Get-WmiObject -class Win32_SystemDriver -computername $vm -namespace $namespace | + where-object { $_.Name -match $componentPattern } | + Format-Table -Auto Name,State,StartMode,DisplayName + ) +} diff --git a/Scripts/Horizon-GetUsageStats.ps1 b/Scripts/Horizon-GetUsageStats.ps1 new file mode 100644 index 0000000..7c389bb --- /dev/null +++ b/Scripts/Horizon-GetUsageStats.ps1 @@ -0,0 +1,38 @@ +<# +.NOTES +Script name: Horizon-UsageStats.ps1 +Author: Ray Heffer, @rayheffer +Last Edited on: 04/18/2017 +Dependencies: PowerCLI 6.5 R1 or later, Horizon 7.0.2 or later +.DESCRIPTION +This is a sample script that retrieves the Horizon usage statistics. This produces the same metrics as listed under View Configuration > Product Licensing and Usage. Service providers can use this script or incorporate it with their existing scripts to automate the reporting of Horizon usage. + +Example Output: +NumConnections : 180 +NumConnectionsHigh : 250 +NumViewComposerConnections : 0 +NumViewComposerConnectionsHigh : 0 +NumTunneledSessions : 0 +NumPSGSessions : 180 +#> + +# User Configuration +$hzUser = "Administrator" +$hzPass = "VMware1!" +$hzDomain = "vmw.lab" +$hzConn = "connect01.vmw.lab" + +# Import the Horizon module +Import-Module VMware.VimAutomation.HorizonView + +# Establish connection to Connection Server +$hvServer = Connect-HVServer -server $hzConn -User $hzUser -Password $hzPass -Domain $hzDomain + +# Assign a variable to obtain the API Extension Data +$hvServices = $Global:DefaultHVServers.ExtensionData + +# Retrieve Connection Server Health metrics +$hvHealth =$hvServices.ConnectionServerHealth.ConnectionServerHealth_List() + +# Display ConnectionData (Usage stats) +$hvHealth.ConnectionData diff --git a/Scripts/PowerCLI_FixNestedFolders.ps1 b/Scripts/PowerCLI_FixNestedFolders.ps1 new file mode 100644 index 0000000..a4e8afe --- /dev/null +++ b/Scripts/PowerCLI_FixNestedFolders.ps1 @@ -0,0 +1,96 @@ +<# +Script name: PowerCLI_FixNestedFolders.ps1 +Created on: 01/11/2018 +Author: Kyle Ruddy, @kmruddy, thatcouldbeaproblem.com +Description: The purpose of the script is to remove the nested Version based folders when using Powercli on systems using older versions of PowerShell +Dependencies: None known + +===Tested Against Environment==== +PowerCLI Version: PowerCLI 6.5.4 +PowerShell Version: 5.1, 4.0 +OS Version: Server 2016, Server 2012 R2 +#> + +# Variable used to store where the PowerCLI module folders exist +$pcliFolder = @() + +# Variable used to store the current PSModulePath locations +$downloadDir = $env:PSModulePath.Split(';') + +# Loop to detect PowerCLI module folders in any of the PSModulePath locations +foreach ($possPath in $downloadDir) { + + # Verifying the PSModulePath location exists + if ((Test-Path -Path $possPath) -eq $true) { + + # Searching for folders with the name of 'VMware.*' + $tempFolder = Get-ChildItem -Path $possPath -Name "VMware.*" + + # If a VMware.* module folder is found, the full path is added to the pcliFolder variable + if ($tempFolder) { + + foreach ($moduleName in $tempFolder) { + $pcliFolder += $possPath + "\" + $moduleName + } + } + + } +} + +# Verifying that there were PowerCLI module folders found +if ($pcliFolder) { + + # Looping through each of the found PowerCLI module folders + foreach ($dir in $pcliFolder) { + + # Variable to be used if there are several PowerCLI module versions available + $historicDir = $null + + # Varibale used to store the PowerCLI module version folder + $tempDir = Get-ChildItem -Path $dir + + # Verifying whether or not there are several PowerCLI module versions available by checking for a type of 'array' + if ($tempDir -is [array]) { + + # Variable used to store the current folder structure + $historicDir = $tempDir + # Updating the tempDir variable to only contain the newest PowerCLI module version folder + $tempDir = $tempDir | Sort-Object Name -Descending | select-Object -First 1 + + } + + # Verifying the child item is indeed a folder + if ($tempDir.GetType().Name -eq "DirectoryInfo") { + + # Obtaining the child objects of the PowerCLI module version folder and copying them to the parent folder + $tempDir | Get-ChildItem | Copy-Item -Destination $dir -ErrorAction Stop + + # Checking for any nested folders within the PowerCLI module version folder + if ($tempDir | Get-ChildItem -Directory) { + + # Obtaining and storing the child items to a variable, then copying the items to the parent folder's nested folder + $nestFolder = $tempDir | Get-ChildItem -Directory + foreach ($nestDir in $nestFolder) { + $nestDir | Get-ChildItem | Copy-Item -Destination ($dir + "\" + $nestDir.Name) -ErrorAction Stop + } + + } + + # Detecting whether the historicDir variable was used + if ($historicDir) { + + # Removing any of the former, no longer needed, directory structure + $historicDir | Remove-Item -Recurse -Force + + } + else { + + # Removing any of the former, no longer needed, directory structure + $tempDir | Remove-Item -Recurse -Force + + } + } + } + +} +else {Write-Host 'No PowerCLI module folders founds in the $PSModulePath directories.'} \ No newline at end of file diff --git a/Scripts/ReadVMSnapshotConfig.ps1 b/Scripts/ReadVMSnapshotConfig.ps1 new file mode 100644 index 0000000..fe274ee --- /dev/null +++ b/Scripts/ReadVMSnapshotConfig.ps1 @@ -0,0 +1,104 @@ +function Get-VMSnapshotConfigContent { +<# + .SYNOPSIS + Reads .vmsd file content + + .DESCRIPTION + Build the vmsd file http URI following https://code.vmware.com/apis/358/vsphere#/doc/vim.FileManager.html + and reads its content with the session established by Connect-VIServer + + .INPUTS + VirtualMachine + + .OUTPUTS + String - the content of the vmsd file + + .NOTES + Author: Dimitar Milov + Version: 1.0 + + .EXAMPLE + Get-VM | Get-VMSnapshotConfigContent +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [ValidateNotNull()] + [VMware.VimAutomation.Types.VirtualMachine] + $VM +) + +PROCESS { + # Create web client from current session + $sessionKey = $vm.GetClient().ConnectivityService.CurrentUserSession.SoapSessionKey + $certValidationHandler = $vm.GetClient().ConnectivityService.GetValidationHandlerForCurrentServer() + $webClient = [vmware.vimautomation.common.util10.httpclientUtil]::CreateHttpClientWithSessionReuse($certValidationHandler, $sessionKey, $null) + + # Build VMSD file http URI + # https://code.vmware.com/apis/358/vsphere#/doc/vim.FileManager.html + $vmName = $vm.Name + $datastoreName = ($vm | Get-Datastore).Name + $dcName = ($vm | Get-Datacenter).Name + $serverAddress = $vm.GetClient().ConnectivityService.ServerAddress + $vmsdUri = [uri]"https://$serverAddress/folder/$vmName/$vmName.vmsd?dcPath=$dcName&dsName=$datastoreName" + + # Get VMSD content as string + $task = $webClient.GetAsync($vmsdUri) + $task.Wait() + $vmsdContent = $task.Result.Content.ReadAsStringAsync().Result + + # Dispose web client + $webClient.Dispose() + + # Result + $vmsdContent +} + +} + +function Get-VMSnapshotConfigSetting { +<# + .SYNOPSIS + Gets the value of a specified key from the snapshot config file content + + .DESCRIPTION + Reads the VM's snapshot config file and searches for specified key. + If key is found its value is returned as an output + + .INPUTS + VirtualMachine and key + + .OUTPUTS + String - config value for the specified key + + .NOTES + Author: Dimitar Milov + Version: 1.0 + + .EXAMPLE + Get-VM | Get-VMSnapshotConfigSetting -Key "numSentinels" +#> +[CmdletBinding()] +param( + [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [ValidateNotNull()] + [VMware.VimAutomation.Types.VirtualMachine] + $VM, + + [Parameter(Mandatory=$true)] + [ValidateNotNull()] + [string] + $Key +) + +PROCESS { + $content = Get-VMSnapshotConfigContent -vm $vm + + $keyMatch = $content | Select-String ('{0} = "(?.*)"' -f $key) + + if ($keyMatch.Matches -ne $null) { + $keyMatch.Matches[0].Groups["value"].Value + } +} +} diff --git a/Scripts/Sample VMC firewall rules management.ps1 b/Scripts/Sample VMC firewall rules management.ps1 new file mode 100644 index 0000000..b62360b --- /dev/null +++ b/Scripts/Sample VMC firewall rules management.ps1 @@ -0,0 +1,20 @@ +$MyRefreshToken = "XXXX-XXXX-XXXX-XXXX" +Connect-VMC -RefreshToken $MyRefreshToken + +#List the user firewall Rules for MGW +Get-VMCFirewallRule -SDDCName "vGhetto" -OrgName "BashFest - Red Team" -GatewayType MGW + +#List the firewall rules including system firewall rules for MGW +Get-VMCFirewallRule -SDDCName "vGhetto" -OrgName "BashFest - Red Team" -GatewayType MGW -ShowAll + +#Export Firewall Rules from original SDDC +Export-VMCFirewallRule -SDDCName "vGhetto" -OrgName "BashFest - Red Team" -GatewayType MGW -Path ~/Desktop/VMCFirewallRules.json + +#Import Firewall Rules to new SDDC +Import-VMCFirewallRule -SDDCName “Single-Host-SDDC” -OrgName "BashFest - Red Team" -GatewayType MGW -Path ~/Desktop/VMCFirewallRules.json + +#Remove the firewall Rules we just created for the SDDC +$Rules = Get-VMCFirewallRule -SDDCName "Single-Host-SDDC" -OrgName "BashFest - Red Team" -GatewayType MGW +Foreach ($rule in $rules){ + Remove-VMCFirewallRule -SDDCName “Single-Host-SDDC” -OrgName "BashFest - Red Team" -GatewayType MGW -RuleId $rule.id +} \ No newline at end of file diff --git a/Scripts/Set-CustomAttributesInGuestinfo.ps1 b/Scripts/Set-CustomAttributesInGuestinfo.ps1 new file mode 100644 index 0000000..d76d3ee --- /dev/null +++ b/Scripts/Set-CustomAttributesInGuestinfo.ps1 @@ -0,0 +1,84 @@ +<# +.NOTES + Script name: Set-CustomAttributesInGuestinfo.ps1 + Created on: 10/04/2018 + Author: Doug Taliaferro, @virtually_doug + Description: Gets Custom Attributes assigned to a VM and makes them available to the guest OS. + Dependencies: None known + + ===Tested Against Environment==== + vSphere Version: 6.5 + PowerCLI Version: 10.0.0.7893909 + PowerShell Version: 5.1.14409.1005 + OS Version: Windows 7, 10 + Keyword: VM, Attributes, Guestinfo + +.SYNOPSIS + Gets Custom Attributes assigned to a VM and makes them available to the guest OS. + +.DESCRIPTION + Gets the custom attributes assigned to one or more VMs and sets their values in the + VM's 'guestinfo' advanced settings. This makes the attributes available within the + guest OS using VM tools (vmtoolsd.exe) and allows the attributes to be used as metadata + for applications or management agents that run inside the guest. If the attribute name + contains spaces they are removed in naming the advanced setting. + + For example, if a VM has a custom attribute named 'Created On', the advanced setting + becomes: + 'guestinfo.CreatedOn' = '08/08/2018 14:24:17' + + This can be retrieved in the guest OS by running: + vmtoolsd.exe --cmd "info-get guestinfo.CreatedOn" + +.PARAMETER VMs + One or more VMs returned from the Get-VM cmdlet. + +.PARAMETER Attributes + The names of the Custom Attributes to get. If the names contain spaces they must be + enclosed in quotes. The spaces will be removed to name the advanced setting. + +.PARAMETER vCenter + The vCenter server to connect to. Optional if you are already connected. + +.EXAMPLE + .\Set-CustomAttributesInGuestInfo.ps1 -VM (get-vm testvm01) -Attributes 'Created On', 'Created By' + + Gets the custom attributes 'Created On' and 'Created By' for 'testvm01' and sets their + values in 'guestinfo.CreatedOn' and 'guestinfo.CreatedBy'. + +.EXAMPLE + .\Set-CustomAttributesInGuestInfo.ps1-VM (get-cluster Dev-01 | get-vm) -Attributes 'Created On' + + Gets the custom attribute 'Created On' for all VMs in the Dev-01 cluster and sets 'guestinfo.CreatedOn' + on each VM. +#> +#Requires -modules VMware.VimAutomation.Core +[CmdletBinding()] +param ( + [Parameter(Mandatory=$true,Position=0)] + $VMs, + [Parameter(Mandatory=$true,Position=1)] + [string[]]$Attributes, + [string]$vCenter +) +if ($vCenter) { + Connect-VIServer $vCenter +} + +ForEach ($vm in $VMs) { + ForEach ($attributeName in $Attributes) { + # Get the custom attribute with a matcing key name + $customField = $vm.CustomFields | Where-Object Key -eq $attributeName + if ($customField) { + # Remove white space from the attribute name because the advanced + # setting name cannot contain spaces + $attributeNameNoSpaces = $customField.Key -replace '\s','' + $guestinfoName = "guestinfo.$attributeNameNoSpaces" + $guestinfoValue = $customField.Value + Write-Host "$($vm): setting '$guestinfoName' = '$guestinfoValue'" + New-AdvancedSetting -Entity $vm -Name $guestinfoName -Value $guestinfoValue -Confirm:$false -Force | Out-Null + } else { + Write-Host "$($vm): custom attribute '$attributeName' not set on this VM" + } + } +} diff --git a/Scripts/Set-TagsInGuestinfo.ps1 b/Scripts/Set-TagsInGuestinfo.ps1 new file mode 100644 index 0000000..c245c05 --- /dev/null +++ b/Scripts/Set-TagsInGuestinfo.ps1 @@ -0,0 +1,98 @@ +<# +.NOTES + Script name: Set-TagsInGuestinfo.ps1 + Created on: 10/02/2018 + Author: Doug Taliaferro, @virtually_doug + Description: Gets the vSphere Tags assigned to a VM and makes them available to the guest OS. + Dependencies: None known + + ===Tested Against Environment==== + vSphere Version: 6.5 + PowerCLI Version: 10.0.0.7893909 + PowerShell Version: 5.1.14409.1005 + OS Version: Windows 7, 10 + Keyword: VM, Tags, Guestinfo + +.SYNOPSIS + Gets the vSphere Tags assigned to a VM and makes them available to the guest OS. + +.DESCRIPTION + Gets the tags assigned to one or more VMs from one or more categories and sets the tag values + in the VM's 'guestinfo' advanced settings. This makes the tags available within the guest OS + using VM tools (vmtoolsd.exe) and allows the tags to be used as metadata for applications or + management agents that run inside the guest. + + For example, if a VM has a tag named 'Accounting' from the + category 'Departments', the advanced setting becomes: + guestinfo.Departments = Accounting + + This can be retrieved in the guest OS by running: + vmtoolsd.exe --cmd "info-get guestinfo.Departments" + + If multiple tags are assigned from the same category, they are joined using the specified + delimter (a semicolon by default): + guestinfo.Departments = Accounting;Sales + +.PARAMETER VMs + One or more VMs returned from the Get-VM cmdlet. + +.PARAMETER Categories + The names of tag categories that should be set in the advanced settings. + + .PARAMETER Delimiter + The delimiting character used for multiple tags of the same category. Defaults to a + semicolon. + +.PARAMETER vCenter + The vCenter server to connect to. Optional if you are already connected. + +.EXAMPLE + .\Set-TagsInGuestInfo.ps1 -VM (get-vm testvm01) -Categories Departments, Environment + + Gets tags assigned to 'testvm01' in the Departments and Environment categories and + sets their values in 'guestinfo.Departments' and 'guestinfo.Environment'. + +.EXAMPLE + .\Set-TagsInGuestInfo.ps1 -VM (get-cluster Dev-01 | get-vm) -Categories Departments + + Gets tags assigned to all VMs in the Dev-01 cluster and sets 'guestinfo.Departments' + on each VM. +#> +#Requires -modules VMware.VimAutomation.Core +[CmdletBinding()] +param ( + [Parameter(Mandatory=$true,Position=0)] + $VMs, + [Parameter(Mandatory=$true,Position=1)] + [string[]]$Categories, + [string]$Delimiter = ';', + [string]$vCenter +) +if ($vCenter) { + Connect-VIServer $vCenter +} + +ForEach ($categoryName in $Categories) { + $category = Get-TagCategory -Name $categoryName + if ($category) { + $guestinfoName = "guestinfo.$category" + + # Get Tag assignments for the VMs + $tags = Get-TagAssignment -Entity $VMs -Category $category + + # Group the tags by VM (in this case the Entity property of Group-Object) + $groups = $tags | Group-Object -Property Entity + + # Get each VM and set the guestinfo + ForEach ($item in $groups) { + $vm = get-vm $item.Name + # Multiple tags of the same category are joined + $guestinfoValue = $item.Group.Tag.Name -join $Delimiter + + Write-Host "$($vm): setting '$guestinfoName' = '$guestinfoValue'" + New-AdvancedSetting -Entity $vm -Name $guestinfoName -Value $guestinfoValue -Confirm:$false -Force | Out-Null + } + } else { + Write-Host "Category '$categoryName' was not found." + } +} diff --git a/Scripts/SetClusterMultiPathToRoundRobin.ps1 b/Scripts/SetClusterMultiPathToRoundRobin.ps1 new file mode 100644 index 0000000..396b038 --- /dev/null +++ b/Scripts/SetClusterMultiPathToRoundRobin.ps1 @@ -0,0 +1,23 @@ +<# + Script name: SetClusterMultiPathToRoundRobin.ps1 + Created on: 09/14/2017 + Author: Alan Comstock, @Mr_Uptime + Description: Set the MultiPath policy for FC devices to RoundRobin and IOPS to 1 for all hosts in a cluster based upon the vendor tag. + Dependencies: None known + PowerCLI Version: VMware PowerCLI 6.5 Release 1 build 4624819 + PowerShell Version: 5.1.14393.1532 + OS Version: Windows 10 +#> + +$pathpolicy="RoundRobin" +$iops="1" +$vendor="3PARdata" +$AllESXHosts = Get-VMHost -Location CLUSTERNAME | Sort Name +Foreach ($esxhost in $AllESXHosts) { + Write-Host "Working on" $esxhost + $scsilun = Get-VMhost $esxhost | Get-VMHostHba -Type "FibreChannel" | Get-ScsiLun -LunType disk | Where-Object {$_.Vendor -like $vendor -and ($_.MultipathPolicy -notlike $pathpolicy -or $_.CommandsToSwitchPath -ne $iops)} + if ($scsilun -ne $null){ + Set-ScsiLun -ScsiLun $scsilun -MultipathPolicy $pathpolicy -CommandsToSwitchPath $iops + } +} +#The End diff --git a/Scripts/SetDatastoreTag.ps1 b/Scripts/SetDatastoreTag.ps1 new file mode 100755 index 0000000..23708e5 --- /dev/null +++ b/Scripts/SetDatastoreTag.ps1 @@ -0,0 +1,198 @@ +<# + .SYNOPSIS + A brief description of the file. + + .DESCRIPTION + Given a list of Datastore Names, this script will assign a Tag to them + + .PARAMETER csvFile + String representing the full path of the file + The file must be structured like this: + ----------------------------- + Tag1,Tag2,Tag3,Tag4 + IPv4-iSCSI-SiteA,Tag1,Tag3 + IPv4-NFS-SiteA,Tag2,Tag4 + ... + ----------------------------- + + .NOTES + =========================================================================== + Created on: 31/03/2017 11:16 + Created by: Alessio Rocchi + Organization: VMware + Filename: SetDatastoreTag.ps1 + =========================================================================== +#> +[CmdletBinding()] +param +( + [Parameter(Mandatory = $true, + ValueFromPipeline = $true)] + [ValidateNotNullOrEmpty()] + [System.String]$csvFile, + [Parameter(Mandatory = $true, + ValueFromPipeline = $true)] + [ValidateNotNullOrEmpty()] + [String]$vCenter, + [Parameter(ValueFromPipeline = $true, + Position = 2)] + [AllowNull()] + [String]$Username, + [Parameter(Position = 3)] + [AllowNull()] + [String]$Password +) + +Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue | Out-Null + +class vcConnector : System.IDisposable +{ + [String]$Username + [String]$Password + [String]$vCenter + [PSObject]$server + + static [vcConnector]$instance + + vcConnector($Username, $Password, $vCenter) + { + Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue | Out-Null + + $this.Username = $Username + $this.Password = $Password + $this.vCenter = $vCenter + $this.connect() + } + + vcConnector($vcCredential, $vCenter) + { + Import-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue | Out-Null + + $this.vcCredential = $vcCredential + $this.vCenter = $vCenter + $this.connect() + } + + [void] hidden connect() + { + try + { + if ([String]::IsNullOrEmpty($this.Username) -or [String]::IsNullOrEmpty($this.Password)) + { + $vcCredential = Get-Credential + Connect-VIServer -Server $this.vCenter -Credential $this.vcCredential -WarningAction SilentlyContinue -ErrorAction Stop | Out-Null + } + else + { + Connect-VIServer -Server $this.vCenter -User $this.Username -Password $this.Password -WarningAction SilentlyContinue -ErrorAction Stop + } + Write-Debug("Connected to vCenter: {0}" -f $this.vCenter) + } + catch + { + Write-Error($Error[0].Exception.Message) + exit + } + } + + + [void] Dispose() + { + Write-Debug("Called Dispose Method of Instance: {0}" -f ($this)) + Disconnect-VIServer -WarningAction SilentlyContinue -Server $this.vCenter -Force -Confirm:$false | Out-Null + } + + static [vcConnector] GetInstance() + { + if ([vcConnector]::instance -eq $null) + { + [vcConnector]::instance = [vcConnector]::new() + } + + return [vcConnector]::instance + } +} + +class Content{ + [System.Collections.Generic.List[System.String]]$availableTags + [System.Collections.Generic.List[System.String]]$elements + + Content() + { + } + + Content([String]$filePath) + { + if ((Test-Path -Path $filePath) -eq $false) + { + throw ("Cannot find file: {0}" -f ($filePath)) + } + try + { + # Cast the Get-Content return type to Generic List of Strings in order to avoid fixed-size array + $this.elements = [System.Collections.Generic.List[System.String]](Get-Content -Path $filePath -ea SilentlyContinue -wa SilentlyContinue) + $this.availableTags = $this.elements[0].split(',') + # Delete the first element aka availableTags + $this.elements.RemoveAt(0) + } + catch + { + throw ("Error reading the file: {0}" -f ($filePath)) + } + } +} + +try +{ + $vc = [vcConnector]::new($Username, $Password, $vCenter) + $csvContent = [Content]::new($csvFile) + + Write-Host("Available Tags: {0}" -f ($csvContent.availableTags)) + + foreach ($element in $csvContent.elements) + { + [System.Collections.Generic.List[System.String]]$splittedList = $element.split(',') + # Get the Datastore Name + [System.String]$datastoreName = $splittedList[0] + # Removing Datastore Name + $splittedList.RemoveAt(0) + # Create a List of Tags which will be assigned to the Datastore + [System.Collections.Generic.List[PSObject]]$tagsToAssign = $splittedList | ForEach-Object { Get-Tag -Name $_ } + Write-Host("Tags to assign to Datastore: {0} are: {1}" -f ($datastoreName, $tagsToAssign)) + # Get Datastore object by the given Datastore Name, first field of the the line + $datastore = Get-Datastore -Name $datastoreName -ea Stop + # Iterate the assigned Datastore Tags + foreach ($tag in ($datastore | Get-TagAssignment)) + { + # Check if the current tag is one of the available ones. + if ($tag.Tag.Name -in $csvContent.availableTags) + { + # Remove the current assigned Tag + Write-Host("Removing Tag: {0}" -f ($tag)) + Remove-TagAssignment -TagAssignment $tag -Confirm:$false + } + } + # Finally add the new set of tags to the Datastore + foreach ($tag in $tagsToAssign) + { + Write-Host("Trying to assign Tag: {0} to Datastore: {1}" -f ($tag.Name, $datastoreName)) + # Assign the Tag + New-TagAssignment -Entity $datastore -Tag $tag + } + } +} +catch [VMware.VimAutomation.Sdk.Types.V1.ErrorHandling.VimException.VimException] +{ + Write-Error("VIException: {0}" -f ($Error[0].Exception.Message)) + exit +} +catch +{ + Write-Error $Error[0].Exception.Message + exit +} +finally +{ + # Let be assured that the vc connection will be disposed. + $vc.Dispose() +} diff --git a/Scripts/SetLunReservation.ps1 b/Scripts/SetLunReservation.ps1 new file mode 100644 index 0000000..ae610a0 --- /dev/null +++ b/Scripts/SetLunReservation.ps1 @@ -0,0 +1,103 @@ +<# + .SYNOPSIS + Set a given LUN ID to Perennially Reserved. + + .DESCRIPTION + A description of the file. + + .PARAMETER vCenter + Set vCenter server to connect to + + .PARAMETER Username + Set username to use + + .PARAMETER Password + Set password to be used + + .PARAMETER VirtualMachine + Name of the virtual machine which has the RDM + + .NOTES + =========================================================================== + Created on: 20/03/2017 15:05 + Created by: Alessio Rocchi + Organization: VMware + Filename: SetLunReservation.ps1 + =========================================================================== +#> +param +( + [Parameter(Mandatory = $true, + ValueFromPipeline = $true, + Position = 0)] + [ValidateNotNullOrEmpty()] + [String]$vCenter, + [Parameter(Mandatory = $false, + ValueFromPipeline = $true, + HelpMessage = 'Set vCenter Username')] + [AllowNull()] + [String]$Username, + [Parameter(Mandatory = $false, + ValueFromPipeline = $true, + HelpMessage = 'Set vCenterPassword')] + [AllowNull()] + [String]$Password, + [Parameter(Mandatory = $true, + ValueFromPipeline = $true)] + [ValidateNotNullOrEmpty()] + [String]$VirtualMachine +) + +Import-Module -Name VMware.VimAutomation.Core -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null + +try +{ + if ([String]::IsNullOrEmpty($Username) -or [String]::IsNullOrEmpty($Password)) + { + $vcCredential = Get-Credential + Connect-VIServer -Server $vCenter -Credential $vcCredential -WarningAction SilentlyContinue -ErrorAction Stop | Out-Null + } + else + { + Connect-VIServer -Server $vCenter -User $Username -Password $Password -WarningAction SilentlyContinue -ErrorAction Stop | Out-Null + } +} +catch +{ + Write-Error("Error connecting to vCenter: {0}" -f $vCenter) + exit +} + + +$rDms = Get-HardDisk -DiskType rawPhysical -Vm (Get-VM -Name $VirtualMachine) +$clusterHosts = Get-Cluster -VM $VirtualMachine | Get-VMHost + +$menu = @{ } + +for ($i = 1; $i -le $rDms.count; $i++) +{ + Write-Host("{0}) {1}[{2}]: {3}" -f ($i, $rDms[$i - 1].Name, $rDms[$i - 1].CapacityGB, $rDms[$i - 1].ScsiCanonicalName)) + $menu.Add($i, ($rDms[$i - 1].ScsiCanonicalName)) +} + +[int]$ans = Read-Host 'Which Disk you want to configure?' +$selection = $menu.Item($ans) +write-host("Choosed Disk: {0}" -f $selection) + +$current = 0 +foreach ($vmHost in $clusterHosts) +{ + Write-Progress -Activity "Processing Cluster." -CurrentOperation $vmHost.Name -PercentComplete (($counter / $clusterHosts.count) * 100) + $esxcli = Get-EsxCli -V2 -VMHost $vmHost + $deviceListArgs = $esxcli.storage.core.device.list.CreateArgs() + $deviceListArgs.device = $selection + $esxcli.storage.core.device.list.Invoke($deviceListArgs) | Select-Object Device, IsPerenniallyReserved + $deviceSetArgs = $esxcli.storage.core.device.setconfig.CreateArgs() + $deviceSetArgs.device = $selection + $deviceSetArgs.perenniallyreserved = $true + $esxcli.storage.core.device.setconfig.Invoke($deviceSetArgs) + $counter++ +} + +Disconnect-VIServer -WarningAction SilentlyContinue -Server $vCenter -Force -Confirm:$false + diff --git a/Scripts/SetMultiPathToRoundRobin b/Scripts/SetMultiPathToRoundRobin new file mode 100644 index 0000000..c1a6253 --- /dev/null +++ b/Scripts/SetMultiPathToRoundRobin @@ -0,0 +1,17 @@ +<# +Script name: SetMultiPathToRoundRobin.ps1 +Created on: 09/13/2017 +Author: Alan Comstock, @Mr_Uptime +Description: Set the MultiPath policy for FC devices to RoundRobin +Dependencies: None known +PowerCLI Version: VMware PowerCLI 6.5 Release 1 build 4624819 +PowerShell Version: 5.1.14393.1532 +OS Version: Windows 10 +#> + +#Check a host for any Fibre Channel devices that are not set to Round Robin. Modify to check clusters if needed. +Get-VMhost HOSTNAME | Get-VMHostHba -Type "FibreChannel" | Get-ScsiLun -LunType disk | Where { $_.MultipathPolicy -notlike "RoundRobin" } | Select CanonicalName,MultipathPolicy + +#Set the Multipathing Policy on a host to Round Robin for any Fibre Channel devices that are not Round Robin +$scsilun = Get-VMhost HOSTNAME | Get-VMHostHba -Type "FibreChannel" | Get-ScsiLun -LunType disk | Where { $_.MultipathPolicy -notlike "RoundRobin" } +Set-ScsiLun -ScsiLun $scsilun -MultipathPolicy RoundRobin diff --git a/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Serverless_VM_Deployment/index.html b/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Serverless_VM_Deployment/index.html new file mode 100644 index 0000000..eb02931 --- /dev/null +++ b/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Serverless_VM_Deployment/index.html @@ -0,0 +1,110 @@ + + + + + VMware Cloud on AWS VM Request-O-Matic + + + +
+

VM Request-O-Matic

+
+
+
+

Use this form to create a new VM on VMware Cloud on AWS

+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+ + + + \ No newline at end of file diff --git a/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Serverless_VM_Deployment/readme.txt b/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Serverless_VM_Deployment/readme.txt new file mode 100644 index 0000000..52c09f8 --- /dev/null +++ b/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Serverless_VM_Deployment/readme.txt @@ -0,0 +1,22 @@ +This is a simple 'serverless application' that allows you to create a VM in +an SDDC on VMware Cloud on AWS using a few cool tools including: Lambda, +Cognito, S3, and VMware Cloud on AWS. + +Matt Dreyer +August 16, 2017 + + +To make this work you need to do the following: + +1. Make sure that the vCenter in your SDDC is publicly accessible, or painfully configure Lambda + to run in an VPC and NAT to a specific IP address (which requires even more IAM roles for VPC access). +2. Create a working VM, and then Clone it to an OVF template in Content Library +3. Use the vCenter API browser to discover the UUID of the your OVF template +4. Update the HTML in index.html to match the UUID(s) of the VMs you wish to deploy +5. Create a new Lambda function and upload vm-request-form.zip as your code +6. Create a new Cognito "Federated Identity" for "anonymous access" +7. Update the javascript in index.html to match your new Cognito role +8. Create an S3 bucket and configure it for Webhosting +9. Upload index.html and vmc-sticker.png into your bucket +10. Muck with IAM until Lambda and Cognito get along together + (required Cognito role permissions are AWSLambdaExecute and AWSLambdaRole) \ No newline at end of file diff --git a/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Serverless_VM_Deployment/vm-request-form.zip b/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Serverless_VM_Deployment/vm-request-form.zip new file mode 100644 index 0000000..1ef725f Binary files /dev/null and b/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Serverless_VM_Deployment/vm-request-form.zip differ diff --git a/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Slack_Inventory/slackinventory.py b/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Slack_Inventory/slackinventory.py new file mode 100644 index 0000000..e912795 --- /dev/null +++ b/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Slack_Inventory/slackinventory.py @@ -0,0 +1,193 @@ +""" + +Basic Tests against the Skyscraper API +VMC API documentation available at https://vmc.vmware.com/swagger/index.html#/ +CSP API documentation is available at https://saas.csp.vmware.com/csp/gateway/api-docs +vCenter API documentation is available at https://code.vmware.com/apis/191/vsphere-automation + +Matt Dreyer +August 15, 2017 + +You can install python 3.6 from https://www.python.org/downloads/windows/ + +You can install the dependent python packages locally (handy for Lambda) with: +pip install requests -t . --upgrade +pip install simplejson -t . --upgrade +pip install certifi -t . --upgrade +pip install pyvim -t . --upgrade +pip install datetime -t . --upgrade + +""" + +import requests #need this for Get/Post/Delete +import simplejson as json #need this for JSON +import datetime #need this for a time stamp + +# To use this script you need to create an OAuth Refresh token for your Org +# You can generate an OAuth Refresh Token using the tool at vmc.vmware.com +# https://console.cloud.vmware.com/csp/gateway/portal/#/user/tokens +strAccessKey = "your key goes here" + + +#where are our service end points +strProdURL = "https://vmc.vmware.com" +strCSPProdURL = "https://console.cloud.vmware.com" +slackURL = "https://hooks.slack.com/services/T6Mrrrrr/B6TSrrrrr/RUldlEzzeY0Dy3drrrrrr" + +#make a datestamp +rightnow = str(datetime.datetime.now()) +rightnow = rightnow.split(".")[0] #get rid of miliseconds + + + + +def getAccessToken(myKey): + params = {'refresh_token': myKey} + headers = {'Content-Type': 'application/json'} + response = requests.post('https://console.cloud.vmware.com/csp/gateway/am/api/auth/api-tokens/authorize', params=params, headers=headers) + json_response = response.json() + access_token = json_response['access_token'] + + # debug only +# print(response.status_code) +# print(response.json()) + + return access_token + + + +#-------------------- Figure out which Org we are in +def getTenantID(sessiontoken): + + myHeader = {'csp-auth-token' : sessiontoken} + + response = requests.get( strProdURL + '/vmc/api/orgs', headers=myHeader) + +# debug only +# print(response.status_code) +# print(response.json()) + +# parse the response to grab our tenant id + jsonResponse = response.json() + strTenant = str(jsonResponse[0]['id']) + + return(strTenant) + + +#---------------Login to vCenter and get an API token +# this will only work if the MGW firewall rules are configured appropriately +def vCenterLogin(sddcID, tenantid, sessiontoken): + + #Get the vCenter details from VMC + myHeader = {'csp-auth-token' : sessiontoken} + myURL = strProdURL + "/vmc/api/orgs/" + tenantid + "/sddcs/" + sddcID + response = requests.get(myURL, headers=myHeader) + jsonResponse = response.json() + + vCenterURL = jsonResponse['resource_config']['vc_ip'] + vCenterUsername = jsonResponse['resource_config']['cloud_username'] + vCenterPassword = jsonResponse['resource_config']['cloud_password'] + + + #Now get an API token from vcenter + myURL = vCenterURL + "rest/com/vmware/cis/session" + response = requests.post(myURL, auth=(vCenterUsername,vCenterPassword)) + token = response.json()['value'] + vCenterAuthHeader = {'vmware-api-session-id':token} + + return(vCenterURL, vCenterAuthHeader) + + + + #------------ Get vCenter inventory and post to slack +def getSDDCInventory(sddcID, tenantid, sessiontoken): + + #first we need to get an authentication token from vCenter + vCenterURL, vCenterAuthHeader = vCenterLogin(sddcID, tenantid, sessiontoken) + + #now let's get a VM count + # for all vms use this : myURL = vCenterURL + "rest/vcenter/vm" + # for management vms use this: myURL = vCenterURL + "rest/vcenter/vm?filter.resource_pools=resgroup-54" + # for workload vms use this: myURL = vCenterURL + "rest/vcenter/vm?filter.resource_pools=resgroup-55" + myURL = vCenterURL + "rest/vcenter/vm" + response = requests.get(myURL, headers=vCenterAuthHeader) + + #deal with vAPI wrapping + vms = response.json()['value'] + + poweredon = [] + poweredoff = [] + + for i in vms: + if i['power_state'] == "POWERED_ON": + poweredon.append(i['name']) + else: + poweredoff.append(i['name']) + + vm_on = len(poweredon) + vm_off = len(poweredoff) + + #next let's figure out how much space we have left on the datastore + myURL = vCenterURL + "rest/vcenter/datastore" + response = requests.get(myURL, headers=vCenterAuthHeader) + + #grab the workload datastore + datastore = response.json()['value'][1] + ds_total = int(datastore['capacity']) + ds_free = int(datastore['free_space']) + + usage = int((ds_free / ds_total) * 100) + freeTB = ( ds_free / 1024 / 1024 / 1024 / 1024) + + + jsonSlackMessage = {'text': \ + "SDDC Inventory Report\n" + \ + "\t " + str(vm_on) + " Virtual Machines Running\n" + \ + "\t " + str(vm_off) + " Virtual Machines Powered Off\n" + \ + "\t " + str(usage) + "% Datastore Capacity Remaining (" + str(int(freeTB)) + " TB)"} + + postSlack(slackURL, jsonSlackMessage) + + return() + +#------------------ Post something to Slack +# Slack API info can be found at https://api.slack.com/incoming-webhooks +# https://api.slack.com/tutorials/slack-apps-hello-world +# Need to create a new App using the Slack API App Builder -- it only needs to do one thing - catch a webhook + +def postSlack(slackURL, slackJSONData): + + slackData = json.dumps(slackJSONData) + + myHeader = {'Content-Type': 'application/json'} + response = requests.post(slackURL, slackData, headers=myHeader) + + if response.status_code != 200: + raise ValueError( + 'Request to slack returned an error %s, the response is:\n%s' + % (response.status_code, response.text) + ) + + return + + + + +#-------------------------------------------- +#---------------- Main ---------------------- +#-------------------------------------------- +def lambda_handler(event, context): + + sddcID = " your id goes here" + tenantID = "your tenant goes here" + + #Get our access token + sessiontoken = getAccessToken(strAccessKey) + + #get the inventory and dump it to + getSDDCInventory(sddcID, tenantID, sessiontoken) + + return + +#testing only +#lambda_handler(0, 0) \ No newline at end of file diff --git a/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Slack_Inventory/vmc-inventory-to-slack.mp4 b/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Slack_Inventory/vmc-inventory-to-slack.mp4 new file mode 100644 index 0000000..1a1ea69 Binary files /dev/null and b/Scripts/VMware_Cloud_on_AWS/AWS_Integrations_Examples/Slack_Inventory/vmc-inventory-to-slack.mp4 differ diff --git a/Scripts/VMware_Cloud_on_AWS/Create100VMs.ps1 b/Scripts/VMware_Cloud_on_AWS/Create100VMs.ps1 new file mode 100644 index 0000000..3cfedaa --- /dev/null +++ b/Scripts/VMware_Cloud_on_AWS/Create100VMs.ps1 @@ -0,0 +1,50 @@ +<# + .NOTES + =========================================================================== + Created by: Alan Renouf + Date: March 27, 2018 + Organization: VMware + Blog: virtu-al.net + Twitter: @alanrenouf + =========================================================================== + + .DESCRIPTION + This will allow you to create multiple workloads in the correct locations on VMware Cloud on AWS. + + .Example + $vCenter = "vcenter.sddc-52-53-75-20.vmc.vmware.com" + $vCenterUser = "cloudadmin@vmc.local" + $vCenterPassword = 'VMware1!' + $ResourcePool = "Compute-ResourcePool" + $Datastore = "WorkloadDatastore" + $DestinationFolder = "Workloads" + $Template = "Gold_Linux_Template" + $VMNamePrefix = "NEWVM" + $NumofVMs = 100 + $RunASync = $true #Set this to $True to create the VMs and not wait for the result before starting the next one +#> + +# ------------- VARIABLES SECTION - EDIT THE VARIABLES BELOW ------------- +$vCenter = "vcenter.sddc-123456789.vmc.vmware.com" +$vCenterUser = "cloudadmin@vmc.local" +$vCenterPassword = '123456789' +$ResourcePool = "Compute-ResourcePool" +$Datastore = "WorkloadDatastore" +$DestinationFolder = "Workloads" +$Template = "Gold_Linux_Template" +$VMNamePrefix = "NEWVM" +$NumofVMs = 100 +$RunASync = $true +# ------------- END VARIABLES - DO NOT EDIT BELOW THIS LINE ------------- + +# Connect to VMC vCenter Server +$VCConn = Connect-VIServer -Server $vCenter -User $vCenterUser -Password $vCenterPassword + +1..$NumofVMs | Foreach-Object { + Write-Host "Creating $VMNamePrefix$($_)" + if ($RunASync){ + New-VM -Name "$VMNamePrefix$($_)" -Template $Template -ResourcePool $ResourcePool -Datastore $datastore -Location $DestinationFolder -RunAsync + } Else { + New-VM -Name "$VMNamePrefix$($_)" -Template $Template -ResourcePool $ResourcePool -Datastore $datastore -Location $DestinationFolder + } +} diff --git a/Scripts/VMware_Cloud_on_AWS/L2VPN-vMotion-OnPrem-to-VMC.ps1 b/Scripts/VMware_Cloud_on_AWS/L2VPN-vMotion-OnPrem-to-VMC.ps1 new file mode 100755 index 0000000..dbd04b8 --- /dev/null +++ b/Scripts/VMware_Cloud_on_AWS/L2VPN-vMotion-OnPrem-to-VMC.ps1 @@ -0,0 +1,99 @@ +<# + .NOTES + =========================================================================== + Created by: Brian Graf + Date: January 8, 2018 + Organization: VMware + Blog: brianjgraf.com + Twitter: @vBrianGraf + =========================================================================== + + .DESCRIPTION + This will allow you to vMotion workloads from your on-premises environment to VMware Cloud on AWS. + + .NOTES + PLEASE NOTE THAT THIS REQUIRES L2 Stretch Network between your on-prem environment and VMC. Without the Layer2 VPN, vMotion will not work. + + .Example + # ------------- VARIABLES SECTION - EDIT THE VARIABLES BELOW ------------- + $destinationvCenter = "vcenter.sddc-52-53-75-20.vmc.vmware.com" + $destinationvCenterUser = "clouduser@cloud.local" + $destinationvCenterPassword = 'VMware1!' + $DestinationResourcePool = "Compute-ResourcePool" + $DestinationPortGroup = "L2-Stretch-Network" + $DestinationDatastore = "WorkloadDatastore" + $DestinationFolder = "Workloads" + + $SourcevCenter = "vcsa-tmm-02.utah.lab" # This is your on-prem vCenter + $SourcevCenterUser = "administrator@vsphere.local" + $SourcevCenterPassword = "VMware1!" + + # This is an easy way to select which VMs will vMotion up to VMC. The Asterisk + # acts as a wildcard + $VMs = "BG_Ubuntu_*" +#> + +# ------------- VARIABLES SECTION - EDIT THE VARIABLES BELOW ------------- +$destinationvCenter = "" # This is your VMware Cloud on AWS SDDC +$destinationvCenterUser = "" +$destinationvCenterPassword = '' +$DestinationResourcePool = "" # Name of the resource pool where the VM will be migrated to +$DestinationPortGroup = "" # Portgroup name that the VM will be connected to +$DestinationDatastore = "" # Name of the vSAN datastore +$DestinationFolder = "" # VM folder where the VM will reside + +$SourcevCenter = "" # This is your on-prem vCenter +$SourcevCenterUser = "" +$SourcevCenterPassword = "" + +# This is an easy way to select which VMs will vMotion up to VMC. +$VMs = "" +# ------------- END VARIABLES - DO NOT EDIT BELOW THIS LINE ------------- + +# Connect to VMC Server +$destVCConn = Connect-VIServer -Server $destinationvCenter -User $destinationvCenterUser -Password $destinationvCenterPassword + +# Connect to On-Prem Server +$sourceVCConn = connect-viserver $SourcevCenter -User $SourcevCenterUser -Password $SourcevCenterPassword + +# Start numbering for status updates +$i = 1 + +# Count total VMs selected to move +$CountVMstoMove = (Get-VM $VMs -Server $sourceVCConn).Count + +# For each VM Get the necessary information for the migration +foreach ($VM in (get-VM $VMs -Server $sourceVCConn)) { + +# Get the network adapter information +$networkAdapter = Get-NetworkAdapter -VM $vm -Server $sourceVCConn + +# Get the destination resource pool +$destination = Get-Resourcepool $DestinationResourcePool -Server $destVCConn + +# Get the destination portgroup +$destinationPortGroup = Get-VDPortgroup -Name $DestinationPortGroup -Server $destVCConn + +# Get the destination datastore +$destinationDatastore = Get-Datastore $DestinationDatastore -Server $destVCConn + +# Get the destination folder +$folder = get-folder $DestinationFolder -server $destVCConn + +# Write updates as each VM is being migrated +Write-host "($i of $CountVMsToMove) Moving " -NoNewline +Write-host "$($VM.name) " -NoNewline -ForegroundColor Yellow +Write-host "from " -NoNewline +Write-host "($SourcevCenter) " -NoNewline -ForegroundColor Yellow +Write-host "to " -NoNewline +Write-host "($DestinationvCenter) " -ForegroundColor Yellow + +# The actual vMotion command along with a measurement to time the duration of the vMotion +$Duration = Measure-Command {Move-VM -VM $vm -Destination $destination -NetworkAdapter $networkAdapter -PortGroup $destinationPortGroup -Datastore $destinationDatastore -InventoryLocation $folder | Out-Null} + +# Write the completion string +Write-host " ($i of $CountVMsToMove) Move of $($VM.name) Completed in ($Duration) Minutes!" -ForegroundColor Green + +# Increase our integer by one and move on +$i++ +} diff --git a/Scripts/VMware_Cloud_on_AWS/VMC Example Script.ps1 b/Scripts/VMware_Cloud_on_AWS/VMC Example Script.ps1 new file mode 100755 index 0000000..d1f1fae --- /dev/null +++ b/Scripts/VMware_Cloud_on_AWS/VMC Example Script.ps1 @@ -0,0 +1,34 @@ +#List the commands available for the VMC module +Get-VMCCommand + +#Connect to VMC +$MyRefreshToken = "XXXX-XXXX-XXXX-XXXX" +Connect-VMC -RefreshToken $MyRefreshToken + +#List the Orgs available to us +Get-VMCOrg + +#List the SDDCs +Get-VMCSDDC -Org BashFest* + +#List the Tasks for a particular Org +Get-VMCTask -Org BashFest* | Select-Object task_type, Sub_Status, start_time, End_time, user_name | Sort-Object Start_Time | Format-Table + +#Get the Public IPs for a SDDC +Get-VMCSDDCPublicIPPool -org bashfest* + +#Get all ESXi hosts for given SDDC +Get-VMCVMHost -org bashfest* -Sddc virtu-al + +#Get the credentials of a SDDC so we can login via vSphere cmdlets +Get-VMCSDDCDefaultCredential -org bashfest* -Sddc virtu-al + +#Connect to your VMC vCenter with default creds +Connect-VmcVIServer -org bashfest* -Sddc virtu-al + +#Run some vSphere cmdlets + +#List all VMs from On-Premises and VMC SDDC +Get-VM | Select vCenterServer, Name, PowerState, VMHost + + diff --git a/Scripts/VMware_Cloud_on_AWS/VMWonAWS_1nodeDeployment.ps1 b/Scripts/VMware_Cloud_on_AWS/VMWonAWS_1nodeDeployment.ps1 new file mode 100644 index 0000000..2a41690 --- /dev/null +++ b/Scripts/VMware_Cloud_on_AWS/VMWonAWS_1nodeDeployment.ps1 @@ -0,0 +1,66 @@ +# Author: Kyle Ruddy +# Product: VMware Cloud on AWS +# Description: VMware Cloud on AWS Single Host Deployment Script using PowerCLI +# Requirements: +# - PowerShell 3.x or newer +# - PowerCLI 6.5.4 or newer + +# Set details for SDDC +$oauthToken = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx" +$sddcName = "PowerCLI-1Host-SDDC" +$hostCount = "1" +$awsRegion = "US_WEST_2" +$useAwsAccount = $false + +# --- Deployment code --- +# Connect to VMware Cloud Service +Connect-Vmc -RefreshToken $oauthToken | Out-Null + +# Get ORG ID +$orgSvc = Get-VmcService -Name com.vmware.vmc.orgs +$org = $orgSvc.List() +Write-Output -InputObject "Org: $($org.display_name) ID: $($org.id)" + +# Check to use the already existing AWS account connection +if ($useAwsAccount -eq $true) { + # Get Linked Account ID + $connAcctSvc = Get-VmcService -Name com.vmware.vmc.orgs.account_link.connected_accounts + $connAcctId = $connAcctSvc.get($org.id) | Select-Object -ExpandProperty id + Write-Output -InputObject "Account ID: $connAcctId" + + # Get Subnet ID + $compSubnetSvc = Get-VmcService -Name com.vmware.vmc.orgs.account_link.compatible_subnets + $vpcMap = $compSubnetSvc.Get($org.id, $connAcctId, $region) | Select-Object -ExpandProperty vpc_map + $compSubnets = $vpcMap | Select-Object -ExpandProperty Values | Select-Object -ExpandProperty subnets + $compSubnet = $compSubnets | where {$_.name -ne $null} | Select-Object -first 1 + Write-Output -InputObject "Subnet CIDR $($compSubnet.subnet_cidr_block) ID: $($compSubnet.subnet_id)" +} +elseif ($useAwsAccount -eq $false) { + Write-Output -InputObject "AWS Account Not Configured - you must connect to an AWS account within 14 days of creating this SDDC" +} + +# Deploy the SDDC +$sddcSvc = Get-VmcService com.vmware.vmc.orgs.sddcs +$sddcCreateSpec = $sddcSvc.Help.create.sddc_config.Create() +$sddcCreateSpec.region = $awsRegion +$sddcCreateSpec.Name = $sddcName +$sddcCreateSpec.num_hosts = $hostCount +if ($org.properties.values.sddcTypes) {$sddcCreateSpec.sddc_type = "1NODE"} +$sddcCreateSpec.Provider = "AWS" + +if ($useAwsAccount -eq $true) { + $accountLinkSpec = $sddcSvc.Help.create.sddc_config.account_link_sddc_config.Element.Create() + $accountLinkSpec.connected_account_id = $connAcctId + $custSubId0 = $sddcSvc.Help.create.sddc_config.account_link_sddc_config.Element.customer_subnet_ids.Element.Create() + $custSubId0 = $compSubnet.subnet_id + $accountLinkSpec.customer_subnet_ids.Add($custSubId0) | Out-Null + $sddcCreateSpec.account_link_sddc_config.Add($accountLinkSpec) | Out-Null +} +elseif ($useAwsAccount -eq $false) { + $accountLinkDelaySpec = $sddcSvc.Help.create.sddc_config.account_link_config.delay_account_link.Create() + $accountLinkDelaySpec = $true + $sddcCreateSpec.account_link_config.delay_account_link = $accountLinkDelaySpec +} + +$newSddc = $sddcSvc.create($org.Id, $sddcCreateSpec) +$newSddc | Select-Object resource_id,status,task_type,start_time,task_id \ No newline at end of file diff --git a/Scripts/VMware_Cloud_on_AWS/VMWonAWS_FirewallRuleAccelerator.ps1 b/Scripts/VMware_Cloud_on_AWS/VMWonAWS_FirewallRuleAccelerator.ps1 new file mode 100644 index 0000000..de5b870 --- /dev/null +++ b/Scripts/VMware_Cloud_on_AWS/VMWonAWS_FirewallRuleAccelerator.ps1 @@ -0,0 +1,209 @@ +# Author: Kyle Ruddy +# Product: VMware Cloud on AWS +# Description: VMware Cloud on AWS Firewall Rule Accelerator for PowerCLI +# Requirements: +# - PowerShell 3.x or newer +# - PowerCLI 6.5.4 or newer +# - Use Default IP Addresses +# - Use NSX-V on VMware Cloud on AWS + +#---------- USER VARIABLES ---------------------------------------- + +$oauthToken = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx" +$orgId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx' +$sddcId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx' + +# ---------- DO NOT MODIFY BELOW THIS ------------------------------ + + +Connect-Vmc -RefreshToken $oauthToken | Out-Null + +$orgSvc = Get-VmcService -Name com.vmware.vmc.orgs + +if ($orgId) { + $org = $orgSvc.List() | where {$_.id -eq $orgId} +} +else {$org = $orgSvc.List()} + +if ($org -eq $null) {Write-Output "No Org Found. Exiting."; break} + +$sddcSvc = Get-VmcService -Name com.vmware.vmc.orgs.sddcs + +if ($sddcId) { + $sddc = $sddcSvc.Get($org.id, $sddcId) +} +else {$sddc = $sddcSvc.List($org.id)} + +if ($sddc -eq $null) {Write-Output "No SDDC Found. Exiting."; break} +elseif ($sddc -is [array]) {Write-Output "Multiple SDDCs Found. Please Specify an SDDC ID. Exiting."; break} + +$edgeSvc = Get-VmcService com.vmware.vmc.orgs.sddcs.networks.edges +$mgwEdge = ($edgeSvc.Get($org.id,$sddcId,'gatewayServices') | Select-Object -ExpandProperty edge_page).data | where {$_.id -eq 'edge-1'} + +$ipsecSvc = Get-VmcService com.vmware.vmc.orgs.sddcs.networks.edges.ipsec.config +$ipsecVPN = $ipsecSvc.Get($org.id, $sddcId, $mgwEdge.id) + +$localSubnet = $ipsecVPN.sites.sites.local_subnets.subnets +$vpnSubnet = $ipsecVPN.sites.sites.peer_subnets.subnets +$vcMgmtIP = $sddc.resource_config.vc_management_ip +$vcPublicIP = $sddc.resource_config.vc_public_ip +$esxSubnet = $sddc.resource_config.esx_host_subnet +$ipsecVPNname = $ipsecVPN.sites.sites.name + +function Add-VMCFirewallRule { + <# + .NOTES + =========================================================================== + Created by: Kyle Ruddy + Date: 08/22/2018 + Organization: VMware + Blog: https://www.kmruddy.com + Twitter: @kmruddy + =========================================================================== + .SYNOPSIS + Creates a Firewall Rule for a given SDDC + .DESCRIPTION + Creates a Firewall Rule for a given SDDC + .EXAMPLE + Add-VMCFirewallRule -OrgId -sddcId -FwRuleName -SourceIpAddress -DestIpAddress -Service + + #> + param( + [Parameter(Mandatory=$true)] + [String]$OrgId, + [Parameter(Mandatory=$true)] + [String]$SddcId, + [Parameter(Mandatory=$false)] + [ValidateSet('Management Gateway','Compute Gateway')] + [String]$Edge = 'Management Gateway', + [Parameter(Mandatory=$true)] + [String]$FwRuleName, + [Parameter(Mandatory=$false)] + $SourceIpAddress, + [Parameter(Mandatory=$false)] + $DestIpAddress, + [Parameter(Mandatory=$true)] + [ValidateSet('HTTPS','ICMP','SSO','Provisioning','Any','Remote Console')] + [String]$Service, + [Parameter(Mandatory=$false)] + [ValidateSet('accept')] + $FwAction = 'accept' + + ) + + if ($edge -eq 'Management Gateway') {$EdgeId = 'edge-1'} + elseif ($edge -eq 'Compute Gateway') {$EdgeId = 'edge-2'} + else {Write-Output "No Valid Edge Input Found."} + + $fwRuleSvc = Get-VmcService com.vmware.vmc.orgs.sddcs.networks.edges.firewall.config.rules + + $ruleElementSpec = $fwRuleSvc.Help.add.firewall_rules.firewall_rules.Element.Create() + $fwRules = $fwRuleSvc.Help.add.firewall_rules.Create() + $ruleSpec = $fwRuleSvc.Help.add.firewall_rules.firewall_rules.Create() + + # AppSpec + $appSpec = $fwRuleSvc.Help.add.firewall_rules.firewall_rules.Element.application.Create() + # ServiceSpec + $serviceSpec = $fwRuleSvc.Help.add.firewall_rules.firewall_rules.Element.application.service.Element.Create() + + if ($Service -eq 'HTTPS') { + $protocol = 'TCP' + $port = @("443") + } + elseif ($Service -eq 'ICMP') { + $protocol = 'ICMP' + $icmpType = 'any' + + } + elseif ($Service -eq 'SSO') { + $protocol = 'TCP' + $port = @("7444") + } + elseif ($Service -eq 'Provisioning') { + $protocol = 'TCP' + $port = @("902") + } + elseif ($Service -eq 'Any') { + $protocol = 'Any' + $port = $null + } + elseif ($Service -eq 'Remote Console') { + $protocol = 'TCP' + $port = @("903") + } + else {Write-Output "No Protocol Found."; break} + + $serviceSpec.protocol = $protocol + + # Process ICMP Type from JSON + $icmpType = $null + if($protocol -eq 'ICMP') { + $icmpType = 'any' + } + + if ($icmpType) { + $serviceSpec.icmp_type = $icmpType} + if ($port) { + $serviceSpec.port = $port + $serviceSpec.source_port = @("any") + } + + $addSpec = $ruleElementSpec.application.service.Add($serviceSpec) + + + # Create Source Spec + if($SourceIpAddress) { + $srcSpec = $fwRuleSvc.Help.add.firewall_rules.firewall_rules.Element.source.Create() + $srcSpec.exclude = $false + $srcSpec.ip_address = @($SourceIpAddress) + $ruleElementSpec.source = $srcSpec + } + + + # Create Destination Spec + if($DestIpAddress) { + $destSpec = $fwRuleSvc.Help.add.firewall_rules.firewall_rules.Element.destination.Create() + $destSpec.exclude = $false + $destSpec.ip_address = @($DestIpAddress) + $ruleElementSpec.destination = $destSpec + + } + + + $ruleElementSpec.rule_type = "user" + $ruleElementSpec.enabled = $true + $ruleElementSpec.logging_enabled = $false + + $ruleElementSpec.action = $FwAction + $ruleElementSpec.name = $FwRuleName + + # Add the individual FW rule spec into our overall firewall rules array + Write-Output "Creating VMC Firewall Rule: $FwRuleName" + $ruleSpecAdd = $ruleSpec.Add($ruleElementSpec) + + $fwRules.firewall_rules = $ruleSpec + $fwRuleAdd = $fwRuleSvc.add($orgId,$sddcId,$EdgeId,$fwRules) + +} + + +# vCenter (ANY) to VPN +Add-VMCFirewallRule -OrgId $org.Id -sddcId $sddc.id -FwRuleName "vCenter (ANY) to $ipsecVPNname" -SourceIpAddress $vcMgmtIP -DestIpAddress $vpnSubnet -Service 'Any' + +# ESXi (ANY) to VPN +Add-VMCFirewallRule -OrgId $org.Id -sddcId $sddc.id -FwRuleName "ESXi (ANY) to $ipsecVPNname" -SourceIpAddress $esxSubnet,'10.2.16.0/20' -DestIpAddress $vpnSubnet -Service 'Any' + +# VPN to vCenter (HTTPS) +Add-VMCFirewallRule -OrgId $org.Id -sddcId $sddc.id -FwRuleName "$ipsecVPNname to vCenter (HTTPS)" -SourceIpAddress $vpnSubnet -DestIpAddress $vcMgmtIP -Service 'HTTPS' + +# VPN to vCenter (ICMP) +Add-VMCFirewallRule -OrgId $org.Id -sddcId $sddc.id -FwRuleName "$ipsecVPNname to vCenter (ICMP)" -SourceIpAddress $vpnSubnet -DestIpAddress $vcMgmtIP -Service 'ICMP' + +# VPN to ESXi (Provisioning) +Add-VMCFirewallRule -OrgId $org.Id -sddcId $sddc.id -FwRuleName "$ipsecVPNname to ESXi (Provisioning)" -SourceIpAddress $vpnSubnet -DestIpAddress $esxSubnet,'10.2.16.0/20' -Service 'Provisioning' + +# VPN to ESXi (Remove Console) +Add-VMCFirewallRule -OrgId $org.Id -sddcId $sddc.id -FwRuleName "$ipsecVPNname to ESXi (Remote Console)" -SourceIpAddress $vpnSubnet -DestIpAddress $esxSubnet,'10.2.16.0/20' -Service 'Remote Console' + +# VPN to ESXi (ICMP) +Add-VMCFirewallRule -OrgId $org.Id -sddcId $sddc.id -FwRuleName "$ipsecVPNname to ESXi (ICMP)" -SourceIpAddress $vpnSubnet -DestIpAddress $esxSubnet,'10.2.16.0/20' -Service 'ICMP' \ No newline at end of file diff --git a/Scripts/VMware_Cloud_on_AWS/VMWonAWS_InviteUsers.ps1 b/Scripts/VMware_Cloud_on_AWS/VMWonAWS_InviteUsers.ps1 new file mode 100644 index 0000000..59ddb7a --- /dev/null +++ b/Scripts/VMware_Cloud_on_AWS/VMWonAWS_InviteUsers.ps1 @@ -0,0 +1,114 @@ +<# +.SYNOPSIS + Takes email address input in order to create VMware Cloud on AWS invites for the desired Organization +.DESCRIPTION + Script which can be used to automate the process of adding new users to a specified VMware Cloud on AWS Organization +.NOTES + Author: Kyle Ruddy, @kmruddy, kmruddy.com +.PARAMETER newUserEmail + Plain text email address or array of email addresses +.PARAMETER roleName + Desired role name of the new users, default is Organization Member +.EXAMPLE + PS > ./VMWonAWS_InviteUsers.ps1 -newUserEmail 'testuser@vmware.com' +.EXAMPLE + PS > ./VMWonAWS_InviteUsers.ps1 -newUserEmail $arrayOfEmailAddresses +#> +[CmdletBinding(SupportsShouldProcess=$True)] + param ( + + [Parameter (Mandatory = $True, Position=0)] + $newUserEmail, + [Parameter (Mandatory = $False, Position=1)] + [ValidateSet("Organization Member","Organization Owner","Support User")] + [string]$roleName = "Organization Member" + ) + + # Set Static Variables for your environment + $oauthToken = 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + $orgID = 'xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + + ### DO NOT MODIFY CODE BELOW THIS LINE ### + $inviteReport = @() + $userEmail = @() + + # Email Validation Testing + if ($newUserEmail -is [array]) { + foreach ($email in $newUserEmail) { + try { + $userEmail += [mailAddress]$email | select-object -ExpandProperty Address + } + catch { + Write-Warning "$email is not a valid email address" + } + } + } + else { + try { + $userEmail += [mailAddress]$newUserEmail | select-object -ExpandProperty Address + } + catch { + Write-Warning "$newUserEmail is not a valid email address" + } + } + + if ($userEmail.Count -eq 0) { + Write-Warning "No valid email addresses found." + Break + } + + # Validation and translation of the role name to the role ID + if ($roleName -eq 'Organization Member') { + $orgRoleNames = @("org_member") + } + elseif ($roleName -eq 'Organization Owner') { + $orgRoleNames = @("org_owner") + } + elseif ($roleName -eq 'Support User') { + $orgRoleNames = @("support_user") + } + + # Creating custom objects to start building out the body input + $bodyObj = new-object -TypeName System.Object + $SvcRoleNames = @("vmc-user:full") + $SvcDefinitionLink = '/csp/gateway/slc/api/definitions/external/ybUdoTC05kYFC9ZG560kpsn0I8M_' + $bodyObj | Add-Member -Name 'orgRoleNames' -MemberType Noteproperty -Value $orgRoleNames + $serviceRolesDtos = New-Object -TypeName System.Object + $serviceRolesDtos | Add-Member -Name 'serviceDefinitionLink' -MemberType Noteproperty -Value $SvcDefinitionLink + $serviceRolesDtos | Add-Member -Name 'serviceRoleNames' -MemberType Noteproperty -Value $SvcRoleNames + $bodyObj | Add-Member -Name 'serviceRolesDtos' -MemberType Noteproperty -Value @($serviceRolesDtos) + $bodyObj | Add-Member -Name 'usernames' -MemberType Noteproperty -Value $userEmail + $body = $bodyObj | ConvertTo-Json -Depth 100 + + # Connecting to the REST API service for authentication and then to perform the POST method + $connection = Invoke-WebRequest -Uri "https://console.cloud.vmware.com/csp/gateway/am/api/auth/api-tokens/authorize?refresh_token=$oauthToken" -Method Post + $accesskey = ($connection.content | Convertfrom-json).access_token + $inviteUsers = Invoke-WebRequest -Uri "https://console.cloud.vmware.com/csp/gateway/am/api/orgs/$orgID/invitations" -headers @{"csp-auth-token"="$accesskey"} -Method Post -Body $body -ContentType "application/json" + + # Outputting the successful invite which was just created + $orgInviteRefResponse = Invoke-WebRequest -Uri "https://console.cloud.vmware.com/csp/gateway/am/api/orgs/$orgid/invitations" -headers @{"csp-auth-token"="$accessKey"} -Method Get + if ($orgInviteRefResponse) { + $orgInviteRefObject = $orgInviteRefResponse | ConvertFrom-Json + + foreach ($inviteRef in $orgInviteRefObject) { + $link = $inviteRef.refLink + $orgInviteResponse = Invoke-WebRequest -Uri "https://console.cloud.vmware.com$link" -headers @{"csp-auth-token"="$accessKey"} -Method Get + + $orgInviteObject = $orgInviteResponse.content | ConvertFrom-Json + + foreach ($emailInput in $userEmail) { + + if ($orgInviteObject.username -eq $emailInput) { + $i = New-Object System.Object + $i | Add-Member -Type NoteProperty -Name InviteID -Value $orgInviteObject.refLink.Substring($orgInviteObject.refLink.Length - 36) + $i | Add-Member -Type NoteProperty -Name Username -Value $orgInviteObject.username + $i | Add-Member -Type NoteProperty -Name Status -Value $orgInviteObject.status + $i | Add-Member -Type NoteProperty -Name OrgRoles -Value ($orgInviteObject.OrgRoleNames -join ", ") + $i | Add-Member -Type NoteProperty -Name Requester -Value $orgInviteObject.generatedBy + $inviteReport += $i + } + } + } + } + + return $inviteReport \ No newline at end of file diff --git a/Scripts/VMware_Cloud_on_AWS/XRef-VMC-Services.ps1 b/Scripts/VMware_Cloud_on_AWS/XRef-VMC-Services.ps1 new file mode 100644 index 0000000..eb591bb --- /dev/null +++ b/Scripts/VMware_Cloud_on_AWS/XRef-VMC-Services.ps1 @@ -0,0 +1,37 @@ +$refreshToken = 'your-refresh-token' + +$reportPath = '.\VMC-services.xlsx' + +Connect-Vmc -RefreshToken $refreshToken > $null + +$columns = @{} +$services = Get-VmcService | Sort-Object -Property Name +$services | ForEach-Object -Process { + $_.Help | Get-Member -MemberType NoteProperty | where{'Constants','Documentation' -notcontains $_.Name} | + ForEach-Object -Process { + if(-not $columns.ContainsKey($_.Name)){ + $columns.Add($_.Name,'') + } + } +} +$columns = $columns.Keys | Sort-Object +$report = @() +foreach($service in $services){ + $obj = [ordered]@{ + Name = $service.Name + } + $columns | ForEach-Object -Process { + $obj.Add($_,'') + } + + $service.Help | Get-Member -MemberType NoteProperty | where{'Constants','Documentation' -notcontains $_.Name} | + ForEach-Object -Process { +# $obj.Item($_.Name) = "$($service.Help.$($_.Name).Documentation)" + $obj.Item($_.Name) = "X" + } + $report += New-Object PSObject -Property $obj +} +$report | Export-Excel -Path $reportPath -WorksheetName 'Services' -FreezeTopRow -BoldTopRow -AutoSize -Show + +Disconnect-Vmc -Confirm:$false + diff --git a/Scripts/VSANSmartsData.ps1 b/Scripts/VSANSmartsData.ps1 new file mode 100644 index 0000000..61ef427 --- /dev/null +++ b/Scripts/VSANSmartsData.ps1 @@ -0,0 +1,59 @@ +Function Get-VSANSmartsData { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function retreives SMART drive data using new vSAN + Management 6.6 API. This can also be used outside of vSAN + to query existing SSD devices not being used for vSAN. + .PARAMETER Cluster + The name of a vSAN Cluster + .EXAMPLE + Get-VSANSmartsData -Cluster VSAN-Cluster +#> + param( + [Parameter(Mandatory=$false)][String]$Cluster + ) + + if($global:DefaultVIServer.ExtensionData.Content.About.ApiType -eq "VirtualCenter") { + if(!$cluster) { + Write-Host "Cluster property is required when connecting to vCenter Server" + break + } + + $vchs = Get-VSANView -Id "VsanVcClusterHealthSystem-vsan-cluster-health-system" + $cluster_view = (Get-Cluster -Name $Cluster).ExtensionData.MoRef + $result = $vchs.VsanQueryVcClusterSmartStatsSummary($cluster_view) + } else { + $vhs = Get-VSANView -Id "HostVsanHealthSystem-ha-vsan-health-system" + $result = $vhs.VsanHostQuerySmartStats($null,$true) + } + + $vmhost = $result.Hostname + $smartsData = $result.SmartStats + + Write-Host "`nESXi Host: $vmhost`n" + foreach ($data in $smartsData) { + if($data.stats) { + $stats = $data.stats + Write-Host $data.disk + + $smartsResults = @() + foreach ($stat in $stats) { + $statResult = [pscustomobject] @{ + Parameter = $stat.Parameter; + Value =$stat.Value; + Threshold = $stat.Threshold; + Worst = $stat.Worst + } + $smartsResults+=$statResult + } + $smartsResults | Format-Table + } + } +} \ No newline at end of file diff --git a/Scripts/VSANVersion.ps1 b/Scripts/VSANVersion.ps1 new file mode 100644 index 0000000..45fa7a7 --- /dev/null +++ b/Scripts/VSANVersion.ps1 @@ -0,0 +1,26 @@ +Function Get-VSANVersion { +<# + .NOTES + =========================================================================== + Created by: William Lam + Organization: VMware + Blog: www.virtuallyghetto.com + Twitter: @lamw + =========================================================================== + .DESCRIPTION + This function retreives the vSAN software version for both VC/ESXi + .PARAMETER Cluster + The name of a vSAN Cluster + .EXAMPLE + Get-VSANVersion -Cluster VSAN-Cluster +#> + param( + [Parameter(Mandatory=$true)][String]$Cluster + ) + $vchs = Get-VSANView -Id "VsanVcClusterHealthSystem-vsan-cluster-health-system" + $cluster_view = (Get-Cluster -Name $Cluster).ExtensionData.MoRef + $results = $vchs.VsanVcClusterQueryVerifyHealthSystemVersions($cluster_view) + + Write-Host "`nVC Version:"$results.VcVersion + $results.HostResults | Select Hostname, Version +} diff --git a/Scripts/esxi-image-comparator.ps1 b/Scripts/esxi-image-comparator.ps1 new file mode 100644 index 0000000..a9be557 --- /dev/null +++ b/Scripts/esxi-image-comparator.ps1 @@ -0,0 +1,97 @@ +<# +Script name: esxi-image-comparator.ps1 +Last update: 24 May 2017 +Author: Eric Gray, @eric_gray +Description: Compare contents (VIBs) of multiple VMware ESXi image profiles. +Dependencies: PowerCLI Image Builder (VMware.ImageBuilder,VMware.VimAutomation.Core) +#> + +param( + [switch]$ShowAllVIBs=$false, + [switch]$HideDates=$false, + [switch]$Interactive=$false, + [switch]$Grid=$false, + [string]$ProfileInclude, + [string]$ProfileExclude +) + +$profileList = Get-EsxImageProfile | sort -Property Name + +if ($ProfileInclude) { + $profileList = $profileList | ? Name -Match $ProfileInclude +} + +if ($ProfileExclude) { + $profileList = $profileList | ? Name -NotMatch $ProfileExclude +} + +if ($profileList.Count -eq 0) { + Write-Host "No ESXi image profiles available in current session." + Write-Host "Use Add-EsxSoftwareDepot for each depot zip bundle you would like to compare." + exit 1 +} + +if ($Interactive) { + $keep = @() + Write-Host "Found the following profiles:" -ForegroundColor Yellow + $profileList | % { write-host $_.Name } + + if ($profileList.Count -gt 7) { + Write-Host "Found $($profileList.Count) profiles!" -ForegroundColor Yellow + Write-Host "Note: List filtering is possible through -ProfileInclude / -ProfileExclude" -ForegroundColor DarkGreen + } + + write-host "`nType 'y' next to each profile to compare..." -ForegroundColor Yellow + + foreach ($profile in $profileList) { + $want = Read-Host -Prompt $profile.Name + if ($want.StartsWith("y") ) { + $keep += $profile + } + + } + $profileList = $keep + +} + +# go thru each profile and build a hash of the vib name and hash of profile name + version +$diffResults = @{} +foreach ($profile in $profileList ) { + foreach ($vib in $profile.VibList) { + $vibValue = $vib.Version + if (! $HideDates) { + $vibValue += " "+ $vib.CreationDate.ToShortDateString() + } + $diffResults.($vib.name) += @{$profile.name = $vibValue} + + } + +} + +# create an object that will neatly output as CSV or table +$outputTable=@() +foreach ($row in $diffResults.keys | sort) { + $vibRow = new-object PSObject + $vibRow | add-member -membertype NoteProperty -name "VIB" -Value $row + $valueCounter = @{} + + foreach ($profileName in $profileList.name) { + #populate this hash to decide if all profiles have same version of VIB + $valueCounter.($diffResults.$row.$profileName) = 1 + $vibRow | add-member -membertype NoteProperty -name $profileName -Value $diffResults.$row.$profileName + } + + if ($valueCounter.Count -gt 1 -or $ShowAllVIBs) { + $outputTable += $vibRow + } +} + +# useful for debugging +#$diffResults | ConvertTo-Json +#$outputTable|Export-Csv -Path .\image-diff-results.csv -NoTypeInformation + +if ($Grid) { + $outputTable | Out-GridView -Title "VMware ESXi Image Profile Comparator" +} else { + $outputTable +} \ No newline at end of file diff --git a/Scripts/esxi-image-creator.ps1 b/Scripts/esxi-image-creator.ps1 new file mode 100644 index 0000000..322c6e6 --- /dev/null +++ b/Scripts/esxi-image-creator.ps1 @@ -0,0 +1,108 @@ +<# +Script name: esxi-image-creator.ps1 +Last update: 24 May 2017 +Author: Eric Gray, @eric_gray +Description: Create a VMware ESXi image profile based on + one or more depots and offline driver bundles. +Dependencies: PowerCLI Image Builder (VMware.ImageBuilder,VMware.VimAutomation.Core) +#> + +param( + [switch]$NewestDate = $false, + [switch]$WriteZip = $false, + [switch]$WriteISO = $false, + [switch]$LeaveCurrentDepotsMounted = $false, + [string]$NewProfileName = "Custom Image $(Get-Date -Format "yyyyMMddhhmm")", + [ValidateNotNullOrEmpty()] + [ValidateSet('VMwareCertified','VMwareAccepted','PartnerSupported','CommunitySupported')] + [string]$Acceptance = "VMwareCertified", + [string[]]$Files = "*.zip" +) + +#### Specify optional image fine-tuning here #### +# comma-separated list (array) of VIBs to exclude +$removeVibs = @("tools-light") + +# force specific VIB version to be included, when more than one version is present +# e.g. "net-enic"="2.1.2.71-1OEM.550.0.0.1331820" +$overrideVibs = @{ + # "net-enic"="2.1.2.71-1OEM.550.0.0.1331820", +} + +#### end of optional fine-tuning #### + +# may be desirable to manually mount an online depot in advance, such as for HPE +# e.g. Add-EsxSoftwareDepot http://vibsdepot.hpe.com/index-ecli-650.xml +if (! $LeaveCurrentDepotsMounted) { + Get-EsxSoftwareDepot | Remove-EsxSoftwareDepot +} + +foreach ($depot in Get-ChildItem $Files) { + if ($depot.Name.EndsWith(".zip") ) { + Add-EsxSoftwareDepot $depot.FullName + } else { + Write-Host "Not a zip depot:" $depot.Name + } +} + +if ((Get-EsxImageProfile).count -eq 0) { + write-host "No image profiles found in the selected files" + exit 1 +} + +# either use the native -Newest switch, or try to find latest VIBs by date (NewestDate) +if ($NewestDate) { + $pkgsAll = Get-EsxSoftwarePackage | sort -Property Name,CreationDate -Descending + $pkgsNewestDate=@() + + foreach ($pkg in $pkgsAll) { + if ($pkgsNewestDate.GetEnumerator().name -notcontains $pkg.Name ) { + $pkgsNewestDate += $pkg + } + } + $pkgs = $pkgsNewestDate + +} else { + $pkgs = Get-ESXSoftwarePackage -Newest +} + +# rebuild the package array according to manual fine-tuning +if ($removeVibs) { + Write-Host "`nThe following VIBs will not be included in ${NewProfileName}:" -ForegroundColor Yellow + $removeVibs + $pkgs = $pkgs | ? name -NotIn $removeVibs +} + +foreach ($override in $overrideVibs.keys) { + # check that the override exists, then remove existing and add override + $tmpOver = Get-EsxSoftwarePackage -Name $override -Version $overrideVibs.$override + if ($tmpOver) { + $pkgs = $pkgs | ? name -NotIn $tmpOver.name + $pkgs += $tmpOver + } else { + Write-host "Did not find:" $override $overrideVibs.$override -ForegroundColor Yellow + } +} + +try { + New-EsxImageProfile -NewProfile $NewProfileName -SoftwarePackage $pkgs ` + -Vendor Custom -AcceptanceLevel $Acceptance -Description "Made with esxi-image-creator.ps1" ` + -ErrorAction Stop -ErrorVariable CreationError | Out-Null +} +catch { + Write-Host "Custom image profile $NewProfileName not created." -ForegroundColor Yellow + $CreationError + exit 1 +} + +Write-Host "`nFinished creating $NewProfileName" -ForegroundColor Yellow + +if ($WriteZip) { + Write-Host "Creating zip bundle..." -ForegroundColor Green + Export-EsxImageProfile -ImageProfile $NewProfileName -ExportToBundle -FilePath .\${NewProfileName}.zip -Force +} + +if ($WriteISO) { + Write-Host "Creating ISO image..." -ForegroundColor Green + Export-EsxImageProfile -ImageProfile $NewProfileName -ExportToIso -FilePath .\${NewProfileName}.iso -Force +} diff --git a/Scripts/get-peakvms.ps1 b/Scripts/get-peakvms.ps1 new file mode 100644 index 0000000..79d7eb3 --- /dev/null +++ b/Scripts/get-peakvms.ps1 @@ -0,0 +1,181 @@ +<# +Script name: get-peakvms.ps1 +Created on: 09/06/2016 +Author: Scott White, @soggychipsnz, https://github.com/LatexGolem +Description: This script will interrogate vcenter to find the peak users of network or storage usage. +This was quite handy for me to quickly identify the source of issues such as an inbound DDOS, outbound DOS or a server pummiling storage due to swapping/etc. +I would suggest that when you first run the script you run it in host mode, which will identify where the hotspot is. Then once the hot host has been identified, run it in VM mode. +For each statistic the number of operations/packets is collectioned along ith the throughput split into reads or writes/sent or recieved - giving you peak and 95th percentiles +Really need to get around to tidying this up :) +Results will be outputted to OGV and into a timestamped CSV in the current working directory +Dependencies: Nothing special. Assumes vsphere logging has 20 second samples for the last 60 minutes and 5 minute samples in the past 24 hours. +#> + +$clusterfilter = "*" + +#Gather basic stuff +do{ + $modes = @("host","vm") + $mode = $modes | OGV -PassThru -Title "Select mode of operation" + $stats = @("Disk Throughput","Network Throughput") + $stat = $stats | OGV -PassThru -Title "Select type of Stat to check" + if (($mode.count -eq 2) -or ($stat.count -eq 2) ){write-host "Please select only a single mode of operation and statistic type."} +}while ($mode.count -eq 2 -and $stat.count -eq 2) + +$hour = 1..24 +$hour = $hour | OGV -PassThru -Title "Select number of hours to go back in time" + + +#Helper Stuff +$start = (Get-Date).addHours(-$hour) +$finish = (Get-Date) +$duration = $finish - $start | %{[int]$_.TotalSeconds} +#If Start is within the last hour, use 20 second sampling otherwise 5 minute avg +if (((Get-Date) - $start ).TotalSeconds -gt 3700) {$interval=300} else {$interval=20} + +function getstats($starttime,$endtime,$sample,$stat,$entity){ + (Get-Stat -Entity $entity -Stat $stat -IntervalSecs $sample -Start $starttime -Finish $endtime).value | measure -Sum | %{$_.sum} +} + + +if ($mode -eq "host"){ + $clusters = get-cluster $clusterfilter + $vmhosts = $clusters | OGV -PassThru -Title "Select Cluster(s) to target, to get all member Hosts"| get-vmhost + + if ($stat -eq "Network Throughput"){ + $master = @() + $vmhosts | %{ + $metric ="net.packetsRx.summation" + $pktrx = getstats $start $finish $interval $metric $_ + + $metric ="net.packetsTx.summation" + $pkttx = getstats $start $finish $interval $metric $_ + + $metric ="net.bytesRx.average" + $bytesrx = getstats $start $finish $interval $metric $_ + + $metric ="net.bytesTx.average" + $bytestx = getstats $start $finish $interval $metric $_ + + $row = "" | select name,pktrx,pkttx,bytesrx,bytestx + $row.name = $_.name + $row.pktrx = $pktrx + $row.pkttx = $pkttx + $row.bytesrx = $bytesrx + $row.bytestx = $bytestx + $master += $row + } + $master | ogv #sort -Property name | Format-Table + } + if ($stat -eq "Disk Throughput"){ + #Target the datastore, just one. + $datastore = Get-Datastore -vmhost $vmhost[0]| OGV -PassThru -Title "Select target datastore" + #Yes this is fugly. + $instance = ($datastore | Get-View).Info.Url.Split("/") | select -last 2 | select -First 1 + + $master = @() + $vmhosts | %{ + $metric ="datastore.datastoreReadIops.latest" + $rop = (Get-Stat -Entity $_ -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish | ?{$_.Instance -match $instance}).value | measure -Sum | %{$_.sum} + + $metric ="datastore.datastoreWriteIops.latest" + $wrop = (Get-Stat -Entity $_ -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish | ?{$_.Instance -match $instance}).value | measure -Sum | %{$_.sum} + + $metric ="datastore.datastoreReadBytes.latest" + $rbytes = (Get-Stat -Entity $_ -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish | ?{$_.Instance -match $instance}).value | measure -Sum | %{$_.sum} + + $metric ="datastore.datastoreWriteBytes.latest" + $wrbytes = (Get-Stat -Entity $_ -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish | ?{$_.Instance -match $instance}).value | measure -Sum | %{$_.sum} + + $row = "" | select name,rop,wrop,rbytes,wrbytes + $row.name = $_.name + $row.rop = $rop + $row.wrop = $wrop + $row.rbytes = $rbytes + $row.wrbytes = $wrbytes + $master += $row + } + $master | ogv + } + +}if ($mode -eq "vm"){ + Write-Host "Do note doing things on a vmbasis take quite some time (please isolate on a host basis first)" + #Currently only works on a cluster basis + $clusters = get-cluster $clusterfilter + $vms = $clusters |get-vmhost | OGV -PassThru -Title "Select Hosts(s) to target, to get all member VMs"| get-vm | ?{$_.powerstate -match "poweredOn"} + + if ($stat -eq "Network Throughput"){ + $master = new-object system.collections.arraylist + $vms | %{ + $metric ="net.packetsRx.summation" + $pktrx = getstats $start $finish $interval $metric $_ + + $metric ="net.packetsTx.summation" + $pkttx = getstats $start $finish $interval $metric $_ + + $metric ="net.bytesRx.average" + $bytesrx = getstats $start $finish $interval $metric $_ + + $metric ="net.bytesTx.average" + $bytestx = getstats $start $finish $interval $metric $_ + + $row = "" | select name,pktrx,pkttx,bytesrx,bytestx + $row.name = $_.name + $row.pktrx = $pktrx + $row.pkttx = $pkttx + $row.bytesrx = $bytesrx + $row.bytestx = $bytestx + $master += $row + } + $master | ogv + } + if ($stat -eq "Disk Throughput"){ + $master = new-object system.collections.arraylist + + $vms = $vms |?{$_.PowerState -eq "PoweredOn"} + foreach ($vm in $vms){ + $row = ""| select name,iops,riops,riops95,rpeak,wiops,wiops95,wpeak,throughputGBpduration,wMBps,rMBps,datastore,used,prov,iops95 + + $metric = "datastore.numberReadAveraged.average" + $rawdata = Get-Stat -Entity $vm -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish + $tmp = $rawdata.value | measure -average -Maximum + $row.riops = [int] $tmp.average + $row.rpeak = [int] $tmp.Maximum + $row.riops95 = ($rawdata.value | sort)[[math]::Round(($rawdata.count-1) * .95)] + + $metric = "datastore.numberwriteaveraged.average" + $rawdata = Get-Stat -Entity $vm -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish + $tmp = $rawdata.value | measure -average -Maximum + $row.wiops = [int] $tmp.average + $row.wpeak = [int] $tmp.Maximum + $row.wiops95 = ($rawdata.value | sort)[[math]::Round(($rawdata.count-1) * .95)] + + $row.iops = ($row.wiops + $row.riops) + + $metric = "datastore.write.average" + $rawdatawr = Get-Stat -Entity $vm -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish + + $metric = "datastore.read.average" + $rawdatar = Get-Stat -Entity $vm -Stat $metric -IntervalSecs $interval -Start $start -Finish $finish + $reads = $rawdatar.value | measure -Sum | %{$_.sum}| %{$_ /($duration/$interval)/1024/1024} + $writes = $rawdatawr.value | measure -Sum | %{$_.sum}| %{$_ /($duration/$interval)/1024/1024} + $total = $reads * $duration + $writes * $duration + + + $row.name = $vm.name + $row.throughputGBpduration = [decimal]::round($total,2) + $row.wMBps = [decimal]::round($writes*1024,2) + $row.rMBps = [decimal]::round($reads*1024,2) + $row.datastore = ($vm.DatastoreIdList[0] | Get-VIObjectByVIView).name #(($vm.DatastoreIdList| select -First 1 | Get-VIObjectByVIView).Name) + + $row.used = [System.Math]::Round(($vm.UsedSpaceGB)) + $row.prov = [System.Math]::Round(($vm.ProvisionedSpaceGB)) + $row.iops95 = $row.riops95 + $row.wiops95 + + $master += $row + } + $master | ogv + } + +} +$master | Export-Csv "$mode$stat-$(get-date -Format HHmm).csv" diff --git a/Scripts/get-ping.ps1 b/Scripts/get-ping.ps1 new file mode 100644 index 0000000..77e3e59 --- /dev/null +++ b/Scripts/get-ping.ps1 @@ -0,0 +1,53 @@ +Function Get-PingStatus + { + param( + [Parameter(ValueFromPipeline=$true)] + [string]$device, + + [validateSet("Online","Offline","ObjectTable")] + [String]$getObject + ) + +begin{ + $hash = @() + + } +process{ + + $device| foreach { + if (Test-Connection $_ -Count 1 -Quiet) { + + if(-not($GetObject)){write-host -ForegroundColor green "Online: $_ "} + + $Hash = $Hash += @{Online="$_"} + }else{ + + if(-not($GetObject)){write-host -ForegroundColor Red "Offline: $_ "} + + $Hash = $Hash += @{Offline="$_"} + } + } + } + +end { + if($GetObject) { + + $Global:Objects = $Hash | foreach { [PSCustomObject]@{ + + DeviceName = $_.Values| foreach { "$_" } + Online = $_.Keys| where {$_ -eq "Online"} + offline = $_.Keys| where {$_ -eq "Offline"} + } + } + + Switch -Exact ($GetObject) + { + + 'Online' { $Global:Objects| where 'online'| select -ExpandProperty DeviceName } + 'Offline' { $Global:Objects| where 'offline'| select -ExpandProperty DeviceName } + 'ObjectTable' { return $Global:Objects } + } + + } + } +} diff --git a/Scripts/ha-vcenter-deploy-template.json b/Scripts/ha-vcenter-deploy-template.json new file mode 100644 index 0000000..3ac0079 --- /dev/null +++ b/Scripts/ha-vcenter-deploy-template.json @@ -0,0 +1,58 @@ +{ + "__version": "0.1", + "__comments": "Configuration for ha-vcenter-deploy.ps1 - www.definit.co.uk", + "target": { + "server": "vcsa.definit.local", + "user": "administrator@vsphere.local", + "password": "VMware1!", + "datacenter": "Lab", + "cluster": "Workload", + "datastore": "vsanDatastore", + "folder": "Nested Labs/HA-vCenter", + "portgroup": "HA-vCenter-Management", + "ha-portgroup": "HA-vCenter-Heartbeat", + "network": { + "netmask": "255.255.255.0", + "gateway": "10.0.11.1", + "prefix": "24", + "dns": "192.168.1.20", + "domain": "definit.local", + "ntp": "192.168.1.1" + } + }, + "sources": { + "VCSAInstaller": "e:\\Pod-Deploy\\vSphere\\VMware-VCSA-all-6.5.0-4944578" + }, + "active": { + "deploymentSize": "small", + "name": "ha-vc-active", + "ip": "10.0.11.10", + "ha-ip": "172.16.1.1", + "hostname": "ha-vc.definit.local", + "rootPassword": "VMware1!", + "sso": { + "domain": "vsphere.local", + "site": "Default-First-Site", + "password": "VMware1!" + }, + "datacenter": "HA-vCenter-Datacenter", + "cluster": "HA-vCenter-Cluster-1", + "distributedSwitch": "HA-vCenter-VDS", + "portgroup": "HA-vCenter-PortGroup" + }, + "cluster": { + "passive-ip": "172.16.1.2", + "passive-name": "ha-vc-passive", + "witness-ip": "172.16.1.3", + "witness-name": "ha-vc-witness", + "ha-mask": "255.255.255.248" + }, + "general": { + "syslog": "192.168.1.26", + "ssh": true, + "log": "ha-vcenter-deploy.log" + }, + "license": { + "vcenter": "7H23H-11111-22222-33333-90ZQN" + } +} \ No newline at end of file diff --git a/Scripts/ha-vcenter-deploy.ps1 b/Scripts/ha-vcenter-deploy.ps1 new file mode 100644 index 0000000..af215dc --- /dev/null +++ b/Scripts/ha-vcenter-deploy.ps1 @@ -0,0 +1,388 @@ +<# +Script name: ha-vcenter-deploy.ps1 +Created on: 30/06/2017 +Author: Sam McGeown, @sammcgeown +Description: The purpose of the script is to deploy vCenter in High Availability mode, using the advanced method. See https://www.definit.co.uk/2017/06/powershell-deploying-vcenter-high-availability-in-advanced-mode/ +Dependencies: None known +#> +param( + [Parameter(Mandatory=$true)] [String]$configFile, + [switch]$deployActive, + [switch]$licenseVCSA, + [switch]$addSecondaryNic, + [switch]$prepareVCHA, + [switch]$clonePassiveVM, + [switch]$cloneWitnessVM, + [switch]$configureVCHA, + [switch]$resizeWitness, + [switch]$createDRSRule +) + +if($psboundparameters.count -eq 1) { + # Only the configFile is passed, set all steps to true + $deployActive = $true + $licenseVCSA = $true + $addSecondaryNic = $true + $prepareVCHA = $true + $clonePassiveVM = $true + $cloneWitnessVM = $true + $configureVCHA = $true + $resizeWitness = $true + $createDRSRule = $true +} + +# Import the PowerCLI and DNS modules +Get-Module -ListAvailable VMware*,DnsServer | Import-Module +if ( !(Get-Module -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) ) { + throw "PowerCLI must be installed" +} +# Written by Sam McGeown @sammcgeown - www.definit.co.uk +# Hat tips and thanks go to... +# William Lam http://www.virtuallyghetto.com/2016/11/vghetto-automated-vsphere-lab-deployment-for-vsphere-6-0u2-vsphere-6-5.html +# http://www.virtuallyghetto.com/2017/01/exploring-new-vcsa-vami-api-wpowercli-part-1.html + +# Get the folder location +$ScriptLocation = Split-Path -Parent $PSCommandPath + +# Import the JSON Config File +$podConfig = (get-content $($configFile) -Raw) | ConvertFrom-Json + +# Path to VCSA Install Sources +$VCSAInstaller = "$($podConfig.sources.VCSAInstaller)" + +# Log File +$verboseLogFile = $podConfig.general.log + +$StartTime = Get-Date + +Function Write-Log { + param( + [Parameter(Mandatory=$true)] + [String]$message, + [switch]$Warning, + [switch]$Info + ) + $timeStamp = Get-Date -Format "dd-MM-yyyy hh:mm:ss" + Write-Host -NoNewline -ForegroundColor White "[$timestamp]" + if($Warning){ + Write-Host -ForegroundColor Yellow " WARNING: $message" + } elseif($Info) { + Write-Host -ForegroundColor White " $message" + }else { + Write-Host -ForegroundColor Green " $message" + } + $logMessage = "[$timeStamp] $message" | Out-File -Append -LiteralPath $verboseLogFile +} + +function Get-VCSAConnection { + param( + [string]$vcsaName, + [string]$vcsaUser, + [string]$vcsaPassword + ) + $existingConnection = $global:DefaultVIServers | where-object -Property Name -eq -Value $vcsaName + if($existingConnection -ne $null) { + return $existingConnection; + } else { + $connection = Connect-VIServer -Server $vcsaName -User $vcsaUser -Password $vcsaPassword -WarningAction SilentlyContinue; + if($connection -ne $null) { + return $connection; + } else { + throw "Unable to connect to $($vcsaName)..." + } + } +} + +function Close-VCSAConnection { + param( + [string]$vcsaName + ) + if($vcsaName.Length -le 0) { + if($Global:DefaultVIServers -ne $null) { + Disconnect-VIServer -Server $Global:DefaultVIServers -Confirm:$false -ErrorAction SilentlyContinue + } + } else { + $existingConnection = $global:DefaultVIServers | where-object -Property Name -eq -Value $vcsaName + if($existingConnection -ne $null) { + Disconnect-VIServer -Server $existingConnection -Confirm:$false; + } else { + Write-Warning -Message "Could not find an existing connection named $($vcsaName)" + } + } +} + +function Get-PodFolder { + param( + $vcsaConnection, + [string]$folderPath + ) + $folderArray = $folderPath.split("/") + $parentFolder = Get-Folder -Server $vcsaConnection -Name vm + foreach($folder in $folderArray) { + $folderExists = Get-Folder -Server $vcsaConnection | Where-Object -Property Name -eq -Value $folder + if($folderExists -ne $null) { + $parentFolder = $folderExists + } else { + $parentFolder = New-Folder -Name $folder -Location $parentFolder + } + } + return $parentFolder +} + + +Close-VCSAConnection + +if($deployActive) { + Write-Log "#### Deploying Active VCSA ####" + $pVCSA = Get-VCSAConnection -vcsaName $podConfig.target.server -vcsaUser $podConfig.target.user -vcsaPassword $podConfig.target.password + $pCluster = Get-Cluster -Name $podConfig.target.cluster -Server $pVCSA + $pDatastore = Get-Datastore -Name $podConfig.target.datastore -Server $pVCSA + $pPortGroup = Get-VDPortgroup -Name $podConfig.target.portgroup -Server $pVCSA + $pFolder = Get-PodFolder -vcsaConnection $pVCSA -folderPath $podConfig.target.folder + + Write-Log "Disabling DRS on $($podConfig.target.cluster)" + $pCluster | Set-Cluster -DrsEnabled:$true -DrsAutomationLevel:PartiallyAutomated -Confirm:$false | Out-File -Append -LiteralPath $verboseLogFile + + + Write-Log "Creating DNS Record" + Add-DnsServerResourceRecordA -Name $podConfig.active.name -ZoneName $podConfig.target.network.domain -AllowUpdateAny -IPv4Address $podConfig.active.ip -ComputerName "192.168.1.20" -CreatePtr -ErrorAction SilentlyContinue + + Write-Log "Deploying VCSA" + $config = (Get-Content -Raw "$($VCSAInstaller)\vcsa-cli-installer\templates\install\embedded_vCSA_on_VC.json") | convertfrom-json + $config.'new.vcsa'.vc.hostname = $podConfig.target.server + $config.'new.vcsa'.vc.username = $podConfig.target.user + $config.'new.vcsa'.vc.password = $podConfig.target.password + $config.'new.vcsa'.vc.datacenter = @($podConfig.target.datacenter) + $config.'new.vcsa'.vc.datastore = $podConfig.target.datastore + $config.'new.vcsa'.vc.target = @($podConfig.target.cluster) + $config.'new.vcsa'.vc.'deployment.network' = $podConfig.target.portgroup + $config.'new.vcsa'.os.'ssh.enable' = $podConfig.general.ssh + $config.'new.vcsa'.os.password = $podConfig.active.rootPassword + $config.'new.vcsa'.appliance.'thin.disk.mode' = $true + $config.'new.vcsa'.appliance.'deployment.option' = $podConfig.active.deploymentSize + $config.'new.vcsa'.appliance.name = $podConfig.active.name + $config.'new.vcsa'.network.'system.name' = $podConfig.active.hostname + $config.'new.vcsa'.network.'ip.family' = "ipv4" + $config.'new.vcsa'.network.mode = "static" + $config.'new.vcsa'.network.ip = $podConfig.active.ip + $config.'new.vcsa'.network.'dns.servers'[0] = $podConfig.target.network.dns + $config.'new.vcsa'.network.prefix = $podConfig.target.network.prefix + $config.'new.vcsa'.network.gateway = $podConfig.target.network.gateway + $config.'new.vcsa'.sso.password = $podConfig.active.sso.password + $config.'new.vcsa'.sso.'domain-name' = $podConfig.active.sso.domain + $config.'new.vcsa'.sso.'site-name' = $podConfig.active.sso.site + + Write-Log "Creating VCSA JSON Configuration file for deployment" + + $config | ConvertTo-Json | Set-Content -Path "$($ENV:Temp)\active.json" + if((Get-VM | Where-Object -Property Name -eq -Value $podConfig.active.name) -eq $null) { + Write-Log "Deploying OVF, this may take a while..." + Invoke-Expression "$($VCSAInstaller)\vcsa-cli-installer\win32\vcsa-deploy.exe install --no-esx-ssl-verify --accept-eula --acknowledge-ceip $($ENV:Temp)\active.json"| Out-File -Append -LiteralPath $verboseLogFile + $vcsaDeployOutput | Out-File -Append -LiteralPath $verboseLogFile + Write-Log "Moving $($podConfig.active.name) to $($podConfig.target.folder)" + if((Get-VM | where {$_.name -eq $podConfig.active.name}) -eq $null) { + throw "Could not find VCSA VM. The script was unable to find the deployed VCSA" + } + Get-VM -Name $podConfig.active.name | Move-VM -Destination $pFolder | Out-File -Append -LiteralPath $verboseLogFile + } else { + Write-Log "VCSA exists, skipping" -Warning + } + Close-VCSAConnection +} + + +if($licenseVCSA) { + Write-Log "#### Configuring VCSA ####" + Write-Log "Getting connection to the new VCSA" + $nVCSA = Get-VCSAConnection -vcsaName $podConfig.active.ip -vcsaUser "administrator@$($podConfig.active.sso.domain)" -vcsaPassword $podConfig.active.sso.password + + Write-Log "Installing vCenter License" + $serviceInstance = Get-View ServiceInstance -Server $nVCSA + $licenseManagerRef=$serviceInstance.Content.LicenseManager + $licenseManager=Get-View $licenseManagerRef + $licenseManager.AddLicense($podConfig.license.vcenter,$null) | Out-File -Append -LiteralPath $verboseLogFile + $licenseAssignmentManager = Get-View $licenseManager.LicenseAssignmentManager + Write-Log "Assigning vCenter Server License" + try { + $licenseAssignmentManager.UpdateAssignedLicense($nVCSA.InstanceUuid, $podConfig.license.vcenter, $null) | Out-File -Append -LiteralPath $verboseLogFile + } + catch { + $ErrorMessage = $_.Exception.Message + Write-Log $ErrorMessage -Warning + } + Close-VCSAConnection -vcsaName $podConfig.active.ip +} + +if($addSecondaryNic) { + Write-Log "#### Adding HA Network Adapter ####" + $pVCSA = Get-VCSAConnection -vcsaName $podConfig.target.server -vcsaUser $podConfig.target.user -vcsaPassword $podConfig.target.password + + if((Get-VM -Server $pVCSA -Name $podConfig.active.name | Get-NetworkAdapter).count -le 1) { + Write-Log "Adding HA interface" + Get-VM -Server $pVCSA -Name $podConfig.active.name | New-NetworkAdapter -Portgroup (Get-VDPortgroup -Name $podConfig.target."ha-portgroup") -Type Vmxnet3 -StartConnected | Out-File -Append -LiteralPath $verboseLogFile + } + Close-VCSAConnection + + $nVCSA = Get-VCSAConnection -vcsaName $podConfig.active.ip -vcsaUser "administrator@$($podConfig.active.sso.domain)" -vcsaPassword $podConfig.active.sso.password + + Write-Log "Configuring HA interface" + $CisServer = Connect-CisServer -Server $podConfig.active.ip -User "administrator@$($podConfig.active.sso.domain)" -Password $podConfig.active.sso.password + + $ipv4API = (Get-CisService -Name 'com.vmware.appliance.techpreview.networking.ipv4') + $specList = $ipv4API.Help.set.config.CreateExample() + $createSpec = [pscustomobject] @{ + address = $podConfig.active."ha-ip"; + default_gateway = ""; + interface_name = "nic1"; + mode = "is_static"; + prefix = "29"; + } + $specList += $createSpec + $ipv4API.set($specList) + Close-VCSAConnection +} + +if($prepareVCHA) { + Write-Log "#### Preparing vCenter HA mode ####" + + $nVCSA = Get-VCSAConnection -vcsaName $podConfig.active.ip -vcsaUser "administrator@$($podConfig.active.sso.domain)" -vcsaPassword $podConfig.active.sso.password + + Write-Log "Preparing vCenter HA" + $ClusterConfig = Get-View failoverClusterConfigurator + + $PassiveIpSpec = New-Object VMware.Vim.CustomizationFixedIp + $PassiveIpSpec.IpAddress = $podConfig.cluster."passive-ip" + + $PassiveNetwork = New-object VMware.Vim.CustomizationIPSettings + $PassiveNetwork.Ip = $PassiveIpSpec + $PassiveNetwork.SubnetMask = $podConfig.cluster."ha-mask" + + $PassiveNetworkSpec = New-Object Vmware.Vim.PassiveNodeNetworkSpec + $PassiveNetworkSpec.IpSettings = $PassiveNetwork + + $WitnessIpSpec = New-Object VMware.Vim.CustomizationFixedIp + $WitnessIpSpec.IpAddress = $podConfig.cluster."witness-ip" + + $WitnessNetwork = New-object VMware.Vim.CustomizationIPSettings + $WitnessNetwork.Ip = $WitnessIpSpec + $WitnessNetwork.SubnetMask = $podConfig.cluster."ha-mask" + + $WitnessNetworkSpec = New-Object VMware.Vim.NodeNetworkSpec + $WitnessNetworkSpec.IpSettings = $WitnessNetwork + + $ClusterNetworkSpec = New-Object VMware.Vim.VchaClusterNetworkSpec + $ClusterNetworkSpec.WitnessNetworkSpec = $WitnessNetworkSpec + $ClusterNetworkSpec.PassiveNetworkSpec = $PassiveNetworkSpec + + $PrepareTask = $ClusterConfig.prepareVcha_task($ClusterNetworkSpec) + + Close-VCSAConnection +} + +if($clonePassiveVM) { + Write-Log "#### Cloning VCSA for Passive Node ####" + + $pVCSA = Get-VCSAConnection -vcsaName $podConfig.target.server -vcsaUser $podConfig.target.user -vcsaPassword $podConfig.target.password + $pVMHost = Get-Random (Get-VMhost -Location $podConfig.target.cluster) + $pFolder = Get-PodFolder -vcsaConnection $pVCSA -folderPath $podConfig.target.folder + + $activeVM = Get-VM -Name $podConfig.active.name + $CloneSpecName = "vCHA_ClonePassive" + + Write-Log "Creating customization spec" + # Clean up any old spec + Get-OSCustomizationSpec -Name $CloneSpecName -ErrorAction SilentlyContinue | Remove-OSCustomizationSpec -Confirm:$false -ErrorAction SilentlyContinue | Out-File -Append -LiteralPath $verboseLogFile + New-OSCustomizationSpec -Name $CloneSpecName -OSType Linux -Domain $podConfig.target.network.domain -NamingScheme fixed -DnsSuffix $podConfig.target.network.domain -NamingPrefix $podConfig.active.hostname -DnsServer $podConfig.target.network.dns -Type NonPersistent | Out-File -Append -LiteralPath $verboseLogFile + Get-OSCustomizationNicMapping -OSCustomizationSpec $CloneSpecName | Set-OSCustomizationNicMapping -IpMode UseStaticIP -IpAddress $podConfig.active.ip -SubnetMask $podConfig.target.network.netmask -DefaultGateway $podConfig.target.network.gateway | Out-File -Append -LiteralPath $verboseLogFile + New-OSCustomizationNicMapping -OSCustomizationSpec $CloneSpecName -IpMode UseStaticIP -IpAddress $podConfig.cluster."passive-ip" -SubnetMask $podConfig.cluster."ha-mask" -DefaultGateway $podConfig.target.network.gateway | Out-File -Append -LiteralPath $verboseLogFile + + Write-Log "Cloning Active VCSA to Passive VCSA" + $passiveVM = New-VM -Name $podConfig.cluster."passive-name" -VM $activeVM -OSCustomizationSpec $CloneSpecName -VMhost $pVMHost -Server $pVCSA -Location $pFolder | Start-VM | Out-File -Append -LiteralPath $verboseLogFile + + # Ensure the network adapters are connected + $passiveVM | Get-NetworkAdapter | Set-NetworkAdapter -Connected:$true -Confirm:$false + + Write-Log "Waiting for VMware Tools" + $passiveVM | Wait-Tools + + Close-VCSAConnection +} + +if($cloneWitnessVM) { + Write-Log "#### Cloning VCSA for Witness Node ####" + + $pVCSA = Get-VCSAConnection -vcsaName $podConfig.target.server -vcsaUser $podConfig.target.user -vcsaPassword $podConfig.target.password + $pVMHost = Get-Random (Get-VMhost -Location $podConfig.target.cluster) + $pFolder = Get-PodFolder -vcsaConnection $pVCSA -folderPath $podConfig.target.folder + + $activeVM = Get-VM -Name $podConfig.active.name + $CloneSpecName = "vCHA_CloneWitness" + + Write-Log "Creating customization spec" + # Clean up any old spec + Get-OSCustomizationSpec -Name $CloneSpecName -ErrorAction SilentlyContinue | Remove-OSCustomizationSpec -Confirm:$false -ErrorAction SilentlyContinue | Out-File -Append -LiteralPath $verboseLogFile + New-OSCustomizationSpec -Name $CloneSpecName -OSType Linux -Domain $podConfig.target.network.domain -NamingScheme fixed -DnsSuffix $podConfig.target.network.domain -NamingPrefix $podConfig.active.hostname -DnsServer $podConfig.target.network.dns -Type NonPersistent | Out-File -Append -LiteralPath $verboseLogFile + New-OSCustomizationNicMapping -OSCustomizationSpec $CloneSpecName -IpMode UseStaticIP -IpAddress $podConfig.cluster."witness-ip" -SubnetMask $podConfig.cluster."ha-mask" -DefaultGateway $podConfig.target.network.gateway | Out-File -Append -LiteralPath $verboseLogFile + + Write-Log "Cloning Active VCSA to Witness VCSA" + $witnessVM = New-VM -Name $podConfig.cluster."witness-name" -VM $activeVM -OSCustomizationSpec $CloneSpecName -VMhost $pVMHost -Server $pVCSA -Location $pFolder | Start-VM | Out-File -Append -LiteralPath $verboseLogFile + + # Ensure the network adapters are connected + $witnessVM | Get-NetworkAdapter | Set-NetworkAdapter -Connected:$true -Confirm:$false + + Write-Log "Waiting for VMware Tools" + $witnessVM | Wait-Tools + + Close-VCSAConnection +} + +if($configureVCHA) { + Write-Log "#### Configuring vCenter HA mode ####" + + $nVCSA = Get-VCSAConnection -vcsaName $podConfig.active.ip -vcsaUser "administrator@$($podConfig.active.sso.domain)" -vcsaPassword $podConfig.active.sso.password + + $ClusterConfig = Get-View failoverClusterConfigurator + $ClusterConfigSpec = New-Object VMware.Vim.VchaClusterConfigSpec + $ClusterConfigSpec.PassiveIp = $podConfig.cluster."passive-ip" + $ClusterConfigSpec.WitnessIp = $podConfig.cluster."witness-ip" + $ConfigureTask = $ClusterConfig.configureVcha_task($ClusterConfigSpec) + Write-Log "Waiting for cluster configuration task" + Start-Sleep -Seconds 30 + + Close-VCSAConnection -vcsaName $podConfig.active.ip +} + +if($resizeWitness) { + Write-Log "#### Resizing Witness Node ####" + $pVCSA = Get-VCSAConnection -vcsaName $podConfig.target.server -vcsaUser $podConfig.target.user -vcsaPassword $podConfig.target.password + + $witnessVM = Get-VM -Name $podConfig.cluster."witness-name" + Write-Log "Waiting for Witness node to shut down" + $witnessVM | Stop-VMGuest -Confirm:$false | Out-File -Append -LiteralPath $verboseLogFile + do { + Start-Sleep -Seconds 3 + $witnessVM = Get-VM -Name $podConfig.cluster."witness-name" + } until($witnessVM.PowerState -eq "Poweredoff") + Write-Log "Setting CPU and Memory" + $witnessVM | Set-VM -MemoryGB 1 -NumCpu 1 -Confirm:$false | Out-File -Append -LiteralPath $verboseLogFile + Write-Log "Starting Witness VM" + $witnessVM | Start-VM | Out-File -Append -LiteralPath $verboseLogFile + Close-VCSAConnection +} + +if($createDRSRule) { + Write-Log "#### Creating DRS Rule ####" + $pVCSA = Get-VCSAConnection -vcsaName $podConfig.target.server -vcsaUser $podConfig.target.user -vcsaPassword $podConfig.target.password + $pCluster = Get-Cluster $podConfig.target.cluster + $vCHA = Get-VM -Name $podConfig.active.name,$podConfig.cluster."passive-name",$podConfig.cluster."witness-name" + New-DRSRule -Name "vCenter HA" -Cluster $pCluster -VM $vCHA -KeepTogether $false | Out-File -Append -LiteralPath $verboseLogFile + Write-Log "Enabling DRS on $($podConfig.target.cluster)" + $pCluster | Set-Cluster -DrsEnabled:$true -DrsAutomationLevel:FullyAutomated -Confirm:$false | Out-File -Append -LiteralPath $verboseLogFile + Close-VCSAConnection +} + + +$EndTime = Get-Date +$duration = [math]::Round((New-TimeSpan -Start $StartTime -End $EndTime).TotalMinutes,2) + +Write-Log "Pod Deployment Completed in $($duration) minutes" \ No newline at end of file diff --git a/Scripts/modules.sh b/Scripts/modules.sh new file mode 100644 index 0000000..ea84779 --- /dev/null +++ b/Scripts/modules.sh @@ -0,0 +1,6 @@ +#!/bin/bash +for file in $( ls /powershell/PowerCLI-Example-Scripts/Modules/ ) +do + mkdir "/root/.local/share/powershell/Modules/${file%.*}/" + mv "/powershell/PowerCLI-Example-Scripts/Modules/$file" "/root/.local/share/powershell/Modules/${file%.*}/$file" +done diff --git a/Scripts/vCenterSnapshot.ps1 b/Scripts/vCenterSnapshot.ps1 new file mode 100644 index 0000000..415aca8 --- /dev/null +++ b/Scripts/vCenterSnapshot.ps1 @@ -0,0 +1,40 @@ +<# + .NOTES + Script name: vCenterSnapshot.ps1 + Created on: 20/09/2017 + Author: Lukas Winn, @lukaswinn + Dependencies: Password is set to VMware123 in my test environment but this can be changed. + + .DESCRIPTION + Script to retrieve snapshot information for all VM's in a given vCenter + +#> +Write-Host "`nGet VM Snapshot Information!" +Write-Host "Copyright 2017 Lukas Winn / @lukaswinn" +Write-Host "Version 1.0" "`n" + +$vCenter = Read-Host -prompt 'Enter FQDN / IP address of vCenter' + +if ($vCenter) { + $vcUser = Read-Host -prompt 'Username' + +Write-Host 'vCenter:' $vCenter '' + +# Connect to vCenter with $vCenter variable value +Connect-VIServer -Server $vCenter -User $vcUser -Password VMware123 + + Write-Host "`nConnected to vCenter: " $vCenter + Write-Host 'Retrieving snapshot information...' + Write-Progress -Activity 'Working...' + + # Get VM snapshot information and output in table format + $getSnap = Get-VM | Get-Snapshot | sort SizeGB -descending | Select VM, Name, Created, @{Label="Size";Expression={"{0:N2} GB" -f ($_.SizeGB)}}, Id + $getSnap | Format-Table | Out-Default + +# Close connection to active vCenter +Disconnect-VIServer $vCenter -Confirm:$false + Write-Host 'Connection closed to' $vCenter +} +else { + Write-Warning "Error: No data entered for vCenter!" +} diff --git a/Scripts/vmCreationNotes.ps1 b/Scripts/vmCreationNotes.ps1 new file mode 100644 index 0000000..2a805ca --- /dev/null +++ b/Scripts/vmCreationNotes.ps1 @@ -0,0 +1,191 @@ +<# +.SYNOPSIS + VM_CreationNotes to replace the Notes on newly deployed virtual machines with + information regarding there deployment, including date, time, user and method + of deployment. + +.DESCRIPTION + VM_CreationNotes is run daily as a scheduled task requiring no interaction. + The script will take in vCenter events for the latest 24 hour period filtering + for vm creation, clone or vapp deployment and parse the data. + Utilizes GET-VIEventsPlus by Luc Dekens for faster event gathering +.NOTES + File Name : VM_CreationNotes.ps1 + Author : KWH + Version : 1.03 + License : GNU GPL 3.0 www.gnu.org/licenses/gpl-3.0.en.html + +.INPUTS + No inputs required +.OUTPUTS + No Output is produced + +.PARAMETER config + No Parameters + +.PARAMETER Outputpath + No Parameters + +.PARAMETER job + No Parameters +.CHANGE LOG + #20170301 KWH - Removed canned VM Initialize script in favor of get-module + #20170303 KWH - Change VIEvent Call to date range rather than maxSamples + # KWH - Optimized event call where to array match + # KWH - Updated Synopsis and Description + # KWH - Changed vcenter list to text file input + # KWH - Added Register Events to list + # KWH - Included Get-VIEventPlus by LucD + #20170321 KWH - Added event $VIEvent array declaration/reset and $VM reset on loops + # KWH - Converted returned events to Local Time +#> + +<# + .SYNOPSIS Function GET-VIEventPlus Returns vSphere events + .DESCRIPTION The function will return vSphere events. With + the available parameters, the execution time can be + improved, compered to the original Get-VIEvent cmdlet. + .NOTES Author: Luc Dekens + .PARAMETER Entity + When specified the function returns events for the + specific vSphere entity. By default events for all + vSphere entities are returned. + .PARAMETER EventType + This parameter limits the returned events to those + specified on this parameter. + .PARAMETER Start + The start date of the events to retrieve + .PARAMETER Finish + The end date of the events to retrieve. + .PARAMETER Recurse + A switch indicating if the events for the children of + the Entity will also be returned + .PARAMETER User + The list of usernames for which events will be returned + .PARAMETER System + A switch that allows the selection of all system events. + .PARAMETER ScheduledTask + The name of a scheduled task for which the events + will be returned + .PARAMETER FullMessage + A switch indicating if the full message shall be compiled. + This switch can improve the execution speed if the full + message is not needed. + .EXAMPLE + PS> Get-VIEventPlus -Entity $vm + .EXAMPLE + PS> Get-VIEventPlus -Entity $cluster -Recurse:$true + #> + function Get-VIEventPlus { + + param( + [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]]$Entity, + [string[]]$EventType, + [DateTime]$Start, + [DateTime]$Finish = (Get-Date), + [switch]$Recurse, + [string[]]$User, + [Switch]$System, + [string]$ScheduledTask, + [switch]$FullMessage = $false + ) + + process { + $eventnumber = 100 + $events = @() + $eventMgr = Get-View EventManager + $eventFilter = New-Object VMware.Vim.EventFilterSpec + $eventFilter.disableFullMessage = ! $FullMessage + $eventFilter.entity = New-Object VMware.Vim.EventFilterSpecByEntity + $eventFilter.entity.recursion = &{if($Recurse){"all"}else{"self"}} + $eventFilter.eventTypeId = $EventType + if($Start -or $Finish){ + $eventFilter.time = New-Object VMware.Vim.EventFilterSpecByTime + if($Start){ + $eventFilter.time.beginTime = $Start + } + if($Finish){ + $eventFilter.time.endTime = $Finish + } + } + if($User -or $System){ + $eventFilter.UserName = New-Object VMware.Vim.EventFilterSpecByUsername + if($User){ + $eventFilter.UserName.userList = $User + } + if($System){ + $eventFilter.UserName.systemUser = $System + } + } + if($ScheduledTask){ + $si = Get-View ServiceInstance + $schTskMgr = Get-View $si.Content.ScheduledTaskManager + $eventFilter.ScheduledTask = Get-View $schTskMgr.ScheduledTask | + where {$_.Info.Name -match $ScheduledTask} | + Select -First 1 | + Select -ExpandProperty MoRef + } + if(!$Entity){ + $Entity = @(Get-Folder -Name Datacenters) + } + $entity | %{ + $eventFilter.entity.entity = $_.ExtensionData.MoRef + $eventCollector = Get-View ($eventMgr.CreateCollectorForEvents($eventFilter)) + $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) + while($eventsBuffer){ + $events += $eventsBuffer + $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) + } + $eventCollector.DestroyCollector() + } + $events + } + } + +#Run parameters - Change below if username or vcenter list source changes +$dayBtwnRuns = 1 +$AdminName = "username" +$credfile = "c:\Scripts\common\credentials\runtime-cred.txt" +$vcfile = "c:\Scripts\Common\inputlists\vcenterlist.txt" + +$vmCreationTypes = @() #Remark out any event types not desired below +$vmCreationTypes += "VmCreatedEvent" +$vmCreationTypes += "VmBeingClonedEvent" +$vmCreationTypes += "VmBeingDeployedEvent" +$vmCreationTypes += "VmRegisteredEvent" +$newline = "`r`n" + +#Convert Password and username to credential object +$password = Get-Content $CredFile | ConvertTo-SecureString +$Cred = New-Object -Typename System.Management.Automation.PSCredential -argumentlist $AdminName,$password + +#Load vCenter List +$vCenterServers = Get-Content $vcfile + +If ($daysBtwnRuns -gt 0) {$daysBtwnRuns = -$daysBtwnRuns} +$Today = Get-Date +$StartDate = ($Today).AddDays($dayBtwnRuns) + +ForEach ($vcenter in $vCenterServers){ + Connect-VIServer $vcenter -Credential $Cred -WarningAction SilentlyContinue -ErrorAction SilentlyContinue + $TargetVM = $null + $VIEvent = @() + $Today = Get-Date + $StartDate = ($Today).AddDays($dayBtwnRuns) + $VIEvent = Get-VIEventPlus -Start $StartDate -Finish $Today -EventType $vmCreationTypes + + $VIEvent|%{ + $NewNote = "" + $VM = $null + $VM = Get-View -Id $_.VM.Vm -Server $vcenter -Property Name,Config + + If ($VM){ + $NewNote = $VM.Config.GuestFullName+$newline + $NewNote += "Deployed: "+$_.CreatedTime.ToLocaltime().DateTime+$newline + $NewNote += "Deployed by "+$_.UserName+$newline + $NewNote += $_.FullFormattedMessage + Set-VM -VM $VM.Name -Notes $NewNote -Confirm:$false + } + } + +Disconnect-VIServer -Confirm:$false}