├── .gitignore ├── Get-ImportStatus.ps1 ├── README.md ├── README.txt ├── Show-DownloadEnabled.ps1 ├── VLP-and-VCD-Metadata-2024.ps1 ├── disable-HOL-Dev-accounts └── disable-HOL-Dev-accounts.ps1 ├── processDevPods ├── input.txt └── processDevPods.ps1 ├── vPod-Mgmt ├── OvfAnalyzer.ps1 ├── ReportDiskChains.ps1 └── VMDK-Hashing.psm1 └── vPodChecker └── vPodChecker-OLD.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | vPod-Mgmt/HOL2017-Licenses.ps1 2 | vPodRouter-OLD 3 | Jenkins/ 4 | disable-HOL-Dev-accounts/disable-HOL-Dev-accounts-2018.ps1 5 | processDevPods/processDevPods-DOUG.ps1 6 | -------------------------------------------------------------------------------- /Get-ImportStatus.ps1: -------------------------------------------------------------------------------- 1 | Function Get-ImportStatus { 2 | <# 3 | .SYNOPSIS 4 | Report the status of an upload into VCD 5 | .DESCRIPTION 6 | Watch the per-file upload of a template 7 | .PARAMETER Template 8 | The name of the vApp Template to be monitored. 9 | .PARAMETER Catalog 10 | The name of the catalog where Template is being uploaded. 11 | .PARAMETER ShowAll 12 | Switch to show all remaining files or only those in progress. 13 | .EXAMPLE 14 | PS C:\> Get-ImportStatus -Catalog "2025-Labs" -Template "HOL-2540-v0.20" 15 | #> 16 | [CmdletBinding()] 17 | param( 18 | [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 19 | $Template, 20 | $Catalog, 21 | [switch]$ShowAll, 22 | [switch]$SummaryOnly 23 | ) 24 | Process { 25 | $BYTES_PER_GB = [Math]::Pow(1024,3) 26 | Foreach ($t in $Template) { 27 | if( $catalog ) { 28 | $cat = Get-catalog $Catalog 29 | $catalog_name = $Catalog 30 | $pods = Get-civapptemplate -Name $Template -Catalog $cat 31 | } else { 32 | $pods = Get-civapptemplate -Name $Template 33 | } 34 | foreach( $vp in $pods ) { 35 | if( $vp.Catalog.Name ) { 36 | $catalog_name = $vp.Catalog.Name 37 | } 38 | else { 39 | $catalog_name = "NONE" 40 | } 41 | Write-Verbose "read $template from $catalog_name" 42 | $all_stats = $vp.ExtensionData.Files.File | Select-Object Name, Size, @{N="SizeGB";E={$_.Size/$BYTES_PER_GB}},BytesTransferred, @{N="TransferredGB";E={$_.BytesTransferred/$BYTES_PER_GB}} 43 | $remaining_stats = $all_stats| Where-Object { $_.Size -ne $_.BytesTransferred } 44 | if( $all_stats.Length -gt 0 ) { 45 | $total_gb_to_transfer = ($all_stats | Measure-Object -Property SizeGB -sum).Sum 46 | $total_gb_remaining = ($remaining_stats | Measure-Object -Property SizeGB -sum).Sum 47 | $total_gb_transferred = ($all_stats | Measure-Object -Property TransferredGB -sum).Sum 48 | #$percent_remaining = 100*($remaining_stats | Measure-Object -property Size -sum).sum / ($vp.ExtensionData.Files.File | Measure-Object -property Size -sum).Sum 49 | Write-Host -ForegroundColor DarkBlue "=== $template in $catalog_name ===================================" 50 | $outline = "Remaining {0:N0} of {1:N0} files, {2:F2} / {3:F2} GB. Finished: ({4:P1})" -f $remaining_stats.Length,($vp.ExtensionData.Files.File).Length,$total_gb_remaining,$total_gb_to_transfer,($total_gb_transferred/$total_gb_to_transfer) 51 | Write-Output $outline 52 | if( -Not $SummaryOnly ) { 53 | if( $ShowAll ) { 54 | $all_stats | Sort-Object -Property "SizeGB" -Descending | Format-Table -AutoSize 55 | } 56 | else { 57 | $remaining_stats | Where-Object { $_.BytesTransferred -ne 0 } | Format-Table -AutoSize 58 | } 59 | } 60 | } 61 | else { 62 | #check for in-flight vdcUploadOvfContents task 63 | $import_tasks = $vp.ExtensionData.Tasks.Task | Where-Object {($_.OperationName -eq "vdcUploadOvfContents") -Or ($_.OperationName -eq "vdcCopyTemplate")} | Select-Object Operation, Status, Progress, StartTime 64 | if( $import_tasks ) { 65 | Write-Host -ForegroundColor Blue "=== $template in $catalog_name ===================================" 66 | foreach ( $import_task in $import_tasks ) { 67 | $outline = "Completed upload; {0} is '{1}' and {2:N0}% complete " -f $import_task.Operation, $import_task.Status, $import_task.Progress 68 | Write-Output $outline 69 | Write-Verbose "Start Time: $($import_task.StartTime)" 70 | $task_duration = $(Get-Date) - $import_task.StartTime 71 | Write-Output $("`tTask has been running for {0:dd} days, {0:hh} hrs, {0:mm} minutes" -f $task_duration) 72 | } 73 | } 74 | else { 75 | Write-Output "`n No upload, import or copy in progress for $Template in $catalog_name" 76 | } 77 | } 78 | } 79 | } 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | hol-ps 2 | ====== 3 | 4 | VMware Hands-on Labs Core team PowerShell Scripts 5 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | This project contains some of the scripts and functions we use within the 2 | VMware Hands-on Labs (http://labs.hol.vmware.com) to manage our mini-datacenters: 3 | 4 | LabStartup is the sleketon we provide to our teams for testing the readiness of 5 | their environments for use by the users. The script can query and restart services, 6 | startup virtual machines and vApps, reports detailed status into a logfile, and 7 | summary progress to the user with DesktopInfo http://www.glenn.delahoy.com/software/ 8 | 9 | The SSL module is still pretty rough but is used by the lab teams to request and 10 | issue certificates for the solutions deployed within our lab environments. We have 11 | a Microsoft CA deployed on Controlcenter (CONTROLCENTER-CA). To save resources in 12 | the lab, we do not have the Microsoft CA web interface enabled, so we use Powershell 13 | to handle the processing. 14 | 15 | The management scripts are some of the tools we use to configure the ESXi hosts and 16 | vCenter servers within the vPods. 17 | 18 | The ModuleSwitcher is a simple panel we use to call Start and Stop scripts to 19 | allow users to "fast forward" their lab environments to defined checkpoints within 20 | the environment -- we call these module boundaries because they map to the states 21 | expected by the various sections of our manuals. 22 | -------------------------------------------------------------------------------- /Show-DownloadEnabled.ps1: -------------------------------------------------------------------------------- 1 | function Show-DownloadEnabled () 2 | { 3 | [CmdletBinding()] 4 | param ( [Parameter (ValueFromPipeline)] 5 | $catalogName 6 | ) 7 | 8 | process 9 | { 10 | $catalog = Get-Catalog -Name $catalogName 11 | $vapps = Get-CIVappTemplate -Catalog $catalog 12 | $enabledTemplates = @() 13 | 14 | foreach ($p in $vapps) { 15 | $pv = Get-CIView $p 16 | $dlHref = $pv.Link | Where-Object {$_.Rel -eq "download:identity"} 17 | Write-Verbose "$($p.name) --- $($dlHref.href)" 18 | if( $dlHref.href -like "http*" ) { 19 | write-Verbose "ENABLED: $($p.name)" 20 | $enabledTemplates += $p.Name 21 | } 22 | } 23 | return $enabledTemplates 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /VLP-and-VCD-Metadata-2024.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # VCD Metadata management from October/November 2024 - Updated for new VCD API 3 | # 4 | # Code works and had been used, but has not been cleaned up for general consumption yet. 5 | # 6 | # In progress, pending completion of VMware Explore 2024/Barcelona 7 | # 8 | # Some of this is based on earlier work from Alan Renouf, but updated fo reflect new VCD API 9 | # 10 | 11 | 12 | 13 | #create a report of current metadata 14 | Function Report-HolMetadata { 15 | param( 16 | [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 17 | [String]$Catalog 18 | ) 19 | $report = @() 20 | $pods = Get-CIVappTemplate -Catalog $Catalog 21 | Write-Host -ForegroundColor Green "Templates read from catalog $Catalog: $($pods.Length)" 22 | foreach ($vp in $pods ) { 23 | $data = Get-CIMetaData -CIObject $vp 24 | $line = "" | Select-Object Pod,Catalog,networkTag1,vappNetwork1 25 | $line.pod = $vp.Name 26 | $line.catalog = $vp.Catalog.Name 27 | $nettag = $data | where { $_.Key -eq "networkTag1" } 28 | if( $nettag ) { $line.networkTag1 = $nettag.Value } 29 | $vappnet = $data | where { $_.Key -eq "vappNetwork1" } 30 | if( $vappnet ) { $line.vappNetwork1 = $vappnet.Value } 31 | $report += $line 32 | } 33 | return $report 34 | } 35 | 36 | #Set the two metadata items used by the VMware Lab Platform to automatically connect deployed vApps to the network 37 | foreach ($vp in $pods ) { 38 | $vappNet = Get-vAppNetName -CiObject $vp 39 | New-CIMetaData -CIObject $vp -Key 'vappNetwork1' -Value $vappNet 40 | New-CIMetaData -CIObject $vp -Key 'networkTag1' -Value 'default' 41 | } 42 | 43 | 44 | # Used by the above loop to get the name of the vApp Network (filters based on known HOL primary vApp network names) 45 | Function Get-vAppNetName { 46 | param( 47 | [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 48 | [PSObject[]]$CIObject 49 | ) 50 | Process { 51 | $sections = $CIObject.ExtensionData.Section 52 | foreach( $s in $sections ) { 53 | if ($s.Network) { 54 | foreach( $network in $s.Network ) { 55 | # Match for HOL-Specific "primary" network names 56 | if( $network.Name -match 'vAppNet' -Or $network.Name -match 'lab_builder_net' -Or $network.Name -match 'VCF-NET' ) { 57 | return $network.Name 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | 65 | ##### 66 | 67 | Function New-CIMetaData { 68 | <# 69 | .SYNOPSIS 70 | Creates a Metadata Key/Value pair. 71 | .DESCRIPTION 72 | Creates a custom Metadata Key/Value pair on a specified vCloud object 73 | .PARAMETER Key 74 | The name of the Metadata to be applied. 75 | .PARAMETER Value 76 | The value of the Metadata to be applied. 77 | .PARAMETER CIObject 78 | The object on which to apply the Metadata. 79 | .EXAMPLE 80 | PS C:\> New-CIMetadata -Key "Owner" -Value "Alan Renouf" -CIObject (Get-Org Org1) 81 | #> 82 | [CmdletBinding( 83 | SupportsShouldProcess=$true, 84 | ConfirmImpact="High" 85 | )] 86 | param( 87 | [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 88 | [PSObject[]]$CIObject, 89 | $Key, 90 | $Value 91 | ) 92 | Process { 93 | Foreach ($Object in $CIObject) { 94 | $Metadata = New-Object VMware.VimAutomation.Cloud.Views.Metadata 95 | $Metadata.MetadataEntry = New-Object VMware.VimAutomation.Cloud.Views.MetadataEntry 96 | $Metadata.MetadataEntry[0].Key = $Key 97 | # $Metadata.MetadataEntry[0].Value = $Value 98 | $Metadata.MetadataEntry[0].TypedValue = New-Object VMware.VimAutomation.Cloud.Views.MetadataStringValue 99 | $Metadata.MetadataEntry[0].TypedValue.Value = $Value 100 | $Object.ExtensionData.CreateMetadata($Metadata) 101 | ($Object.ExtensionData.GetMetadata()).MetadataEntry | Where {$_.Key -eq $key } | Select @{N="CIObject";E={$Object.Name}}, Key, @{N="Value";E={$_.TypedValue.Value}} 102 | } 103 | } 104 | } 105 | 106 | ##### 107 | 108 | Function Get-CIMetaData { 109 | <# 110 | .SYNOPSIS 111 | Retrieves all Metadata Key/Value pairs. 112 | .DESCRIPTION 113 | Retrieves all custom Metadata Key/Value pairs on a specified vCloud object 114 | .PARAMETER CIObject 115 | The object on which to retrieve the Metadata. 116 | .PARAMETER Key 117 | The key to retrieve. 118 | .EXAMPLE 119 | PS C:\> Get-CIMetadata -CIObject (Get-Org Org1) 120 | #> 121 | param( 122 | [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 123 | [PSObject[]]$CIObject, 124 | $Key 125 | ) 126 | Process { 127 | Foreach ($Object in $CIObject) { 128 | If ($Key) { 129 | #($Object.ExtensionData.GetMetadata()).MetadataEntry | Where {$_.Key -eq $key } | Select @{N="CIObject";E={$Object.Name}}, Key, Value 130 | ($Object.ExtensionData.GetMetadata()).MetadataEntry | where {$_.key -eq "networkTag1"} | Select @{N="CIObject";E={$Object.Name}}, Key, @{N="Value";E={$_.TypedValue.Value}} 131 | } Else { 132 | ($Object.ExtensionData.GetMetadata()).MetadataEntry | Select @{N="CIObject";E={$Object.Name}}, Key, @{N="Value";E={$_.TypedValue.Value}} 133 | } 134 | } 135 | } 136 | } 137 | 138 | ##### 139 | 140 | Function Remove-CIMetaData { 141 | <# 142 | .SYNOPSIS 143 | Removes a Metadata Key/Value pair. 144 | .DESCRIPTION 145 | Removes a custom Metadata Key/Value pair on a specified vCloud object 146 | .PARAMETER Key 147 | The name of the Metadata to be removed. 148 | .PARAMETER CIObject 149 | The object on which to remove the Metadata. 150 | .EXAMPLE 151 | PS C:\> Remove-CIMetaData -CIObject (Get-Org Org1) -Key "Owner" 152 | #> 153 | [CmdletBinding( 154 | SupportsShouldProcess=$true, 155 | ConfirmImpact="High" 156 | )] 157 | param( 158 | [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 159 | [PSObject[]]$CIObject, 160 | $Key 161 | ) 162 | Process { 163 | $CIObject | Foreach { 164 | $metadataValue = ($_.ExtensionData.GetMetadata()).GetMetaDataValue($Key) 165 | If($metadataValue) { $metadataValue.Delete() } 166 | } 167 | } 168 | } 169 | 170 | ##### 171 | 172 | Function Remove-CIMetaDataDuplicate { 173 | <# 174 | 175 | HACK 176 | 177 | It is possible to have two metadata items with the same name if one is read-only and the other is R/W 178 | it may have to do with one being in the "system" domain and being managed by the sysadmin 179 | This should remove the duplicate that is R/W 180 | 181 | The command takes two parameters: the VCD object (template) and the name of the metadata key to look for and remove duplicate 182 | #> 183 | param( 184 | [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] 185 | [PSObject[]]$CIObject, 186 | $Key 187 | ) 188 | Process { 189 | Foreach ($Object in $CIObject) { 190 | If ($Key) { 191 | $matches = ($Object.ExtensionData.GetMetadata()).MetadataEntry | Where {$_.Key -eq $key } 192 | If( $matches.Length -ge 2 ) { 193 | Write-Host "`t Duplicate found. Deleting R/W entry from $($Object.Name)" 194 | Remove-CIMetaData -CIObject $Object -Key $Key 195 | #foreach ($entry in $matches) { 196 | # if( -not $entry.Domain ) { 197 | # Write-Host "`t deleting R/W entry from $($Object.Name)" 198 | # $entry.Delete() 199 | # } 200 | #} 201 | } 202 | } Else { 203 | Write-Host "NEED A KEY" 204 | } 205 | } 206 | } 207 | } -------------------------------------------------------------------------------- /disable-HOL-Dev-accounts/disable-HOL-Dev-accounts.ps1: -------------------------------------------------------------------------------- 1 | # disable HOL-Dev accounts with exceptions 2 | 3 | $cloud = 'vcore1-us03.oc.vmware.com' 4 | $devOrgName = 'HOL-Dev' 5 | $devUser = 'scriptuser' 6 | $pass = 'XXXXXXX' 7 | 8 | Try { 9 | Disconnect-CIServer * -Confirm:$false 10 | } Catch {} 11 | 12 | Connect-CIServer $cloud -org $devOrgName -user $devUser -password $pass 13 | 14 | $keep = @{ 15 | # technical marketing 16 | 'ahald' = $true 17 | 'bbazan' = $true 18 | 'bcall' = $true 19 | 'bcall-test' = $true 20 | 'dbaer' = $true 21 | 'devpodman' = $true 22 | 'dmischak' = $true 23 | 'drollins' = $true 24 | 'hmourad' = $true 25 | 'hstagner' = $true 26 | 'joeyd' = $true 27 | 'joeyd-local' = $true 28 | 'jschnee' = $true 29 | 'kgleed' = $true 30 | 'nee-dev' = $true 31 | 'nee-prod' = $true 32 | 'rnoth' = $true 33 | 'scriptuser' = $true 34 | 'sqlworkshopdev' = $true 35 | 'testinguser' = $true 36 | # approved captains/principals 37 | 'jschulman' = $true 38 | 'ksteil' = $true 39 | 'jlafollette' = $true 40 | 'sray' = $true 41 | 'kluck' = $true 42 | 'jsilvagi' = $true 43 | 'gparsons' = $true 44 | 'smomber' = $true 45 | } 46 | 47 | Write-Host "Retrieving user accounts from $devOrgName..." 48 | $ciUsers = Get-CIUser -Org $devOrgName -name 'bcall-test' # only testing 49 | #$ciUsers = Get-CIUser -Org $devOrgName # this is the real call 50 | 51 | Foreach ($ciUser in $ciUsers) { 52 | #Write-Host $ciUser.Name 53 | If ( $keep[$ciUser.Name] ) { 54 | Write-Host "found $ciUser in keep array so will NOT disable" 55 | } Else { 56 | If ( $ciUser.Enabled ) { # because UpdateServerData() takes some time only disable if needed 57 | Write-Host "disabling $ciUser..." 58 | $ciUser.ExtensionData.IsEnabled = $false 59 | $tmp = $ciUser.ExtensionData.UpdateServerData() 60 | } Else { 61 | Write-Host "$ciUser is already disabled." 62 | } 63 | } 64 | } 65 | 66 | Disconnect-CIServer * -Confirm:$false 67 | 68 | ###END -------------------------------------------------------------------------------- /processDevPods/input.txt: -------------------------------------------------------------------------------- 1 | HOL-PRT-1672-v1.1 2 | -------------------------------------------------------------------------------- /processDevPods/processDevPods.ps1: -------------------------------------------------------------------------------- 1 | 2 | Function Add-CIVAppShadows { 3 | <# 4 | Takes a list of vApps and a list of OrgVDCs 5 | Provisions one copy of a vApp on each OrgVdc simultaneously (asynchronously) 6 | Named _shadow_ 7 | With 5 hour storage and Runtime leases -- should cleanup themselves 8 | Waits for the last of the vApps to finish deploying before moving to the next template 9 | 10 | $vApps = @() 11 | $vAppNames | % { $vApps += (Get-CIVAppTemplate $_ -Catalog MY_CATALOG) } 12 | $orgVDCs = @() 13 | $orgVdcNames | % { $orgVDCs += (Get-OrgVDC $_) } 14 | #> 15 | PARAM ( 16 | $vApps=$(throw "need -vApps"), 17 | $OrgVDCs=$(throw "need -OrgVdcs"), 18 | $SleepTime=30, 19 | $Debug=$false 20 | ) 21 | PROCESS { 22 | $fiveHr = New-Object System.Timespan 5,0,0 23 | 24 | foreach( $vApp in $vApps ) { 25 | 26 | #create one shadow on each orgvdc 27 | Write-Host -fore Green "Beginning shadows for $($vApp.Name) at $(Get-Date)" 28 | foreach( $orgVDC in $OrgVDCs ) { 29 | $shadowName = $($($vApp.Name) + "_shadow_" + $($orgVDC.Name)) 30 | #New-CIVApp -Name $shadowName -OrgVdc $orgVDC -VAppTemplate $vApp -RuntimeLease $fiveHr -StorageLease $fiveHr -RunAsync | Out-Null 31 | New-CIVApp -Name $shadowName -OrgVdc $orgVDC -VAppTemplate $vApp -RunAsync | Out-Null 32 | Write-Host "==> Creating $shadowName" 33 | } 34 | #If I pull the "Status" from the object returned by New-vApp it isn't right. This works. 35 | $shadows = @{} 36 | $shadowPattern = $($vApp.Name) + "_shadow_*" 37 | if( $Debug ) { Write-Host -Fore Yellow "DEBUG: looking for $shadowPattern" } 38 | Foreach ($shadow in $(Get-CIVApp $shadowPattern)) { 39 | $shadows.Add( $shadow.Name , $(Get-CIView -CIObject $shadow) ) 40 | } 41 | 42 | #wait for all shadows of this template to complete before starting on next one 43 | while( $shadows.Count -gt 0 ) { 44 | #working around a Powershell quirk related to enumerating and modification 45 | $keys = $shadows.Clone().Keys 46 | 47 | foreach( $key in $keys ) { 48 | $shadows[$key].UpdateViewData() 49 | 50 | if( $shadows[$key].Status -ne 0 ) { 51 | #has completed (usually status=8), remove it from the waitlist 52 | Write-Host "==> Finished $key with status $($shadows[$key].Status), $($shadows.count - 1) to go." 53 | $shadows.Remove($key) 54 | } 55 | } 56 | 57 | #sleep between checks 58 | if( $shadows.Count -gt 0 ) { 59 | Write-Host -Fore Yellow "DEBUG: Sleeping $SleepTime sec at $(Get-Date)" 60 | Sleep -sec $SleepTime 61 | } 62 | } 63 | Write-Host -fore Green "Finished shadows for $($vApp.Name) at $(Get-Date)" 64 | } 65 | } 66 | } #Add-CIVAppShadows 67 | 68 | 69 | Function Add-CIVAppShadowsWait { 70 | <# 71 | Wait for a single template to be "Resolved" then kick off shadows 72 | Quick and dirty... no error checking.. can go infinite if the import fails 73 | #> 74 | PARAM ( 75 | $vApp=$(throw "need -vApps"), 76 | $OrgVDCs=$(throw "need -OrgVdcs"), 77 | $SleepTime=300 78 | ) 79 | 80 | PROCESS { 81 | while ($vApp.status -ne "Resolved") { 82 | write-host "$($vApp.status) : $(($vApp.ExtensionData.Tasks).Task[0].Progress)% complete" 83 | Sleep -sec $SleepTime 84 | $vApp = Get-civapptemplate $vApp.name -catalog $vApp.catalog 85 | } 86 | Add-CIVAppShadows -o $OrgVDCs -v $vApp 87 | } 88 | } #Add-CIVAppShadowsWait 89 | 90 | #Import-Module shadow.psm1 91 | 92 | <# 93 | #Load the VMware PowerCLI tools 94 | Try { 95 | #v5.x 96 | # Add-PSSnapin VMware.VimAutomation.Core -ErrorAction 1 97 | #v6.x 98 | #C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1 99 | $PowerCliInit = 'C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1' 100 | . $PowerCliInit 101 | } 102 | Catch { 103 | Write-Host "No PowerCLI found, unable to continue." 104 | Write-Progress "FAIL - No PowerCLI" 'FAIL-1' 105 | Exit 106 | } 107 | #> 108 | 109 | # BEGIN HERE 110 | 111 | # Change these appropriately 112 | 113 | $cloud = 'vcore1-us03.oc.vmware.com' 114 | $devOrgName = 'HOL-Dev' 115 | $devUser = 'bcall' 116 | $wipCatalogName = 'HOL-Staging' 117 | $wipCatalogName = '_WorkInProgress' 118 | $stageCatalogName = 'HOL-Staging' 119 | #$stageCatalogName = 'Dell Staging ONLY' 120 | #$stageCatalogName = 'HOL 2016 Released Labs' 121 | $prodOrgName = 'HOL' 122 | $prodUser = 'bcall-local' 123 | $prodCatalogName = 'HOL-Masters' 124 | 125 | $input = Get-Content $args[0] 126 | # TODO: set your secret password here 127 | $pass = "CHANGEME" 128 | 129 | Try { 130 | Disconnect-CIServer * -Confirm:$false 131 | } Catch {} 132 | 133 | For ($i=0; $i -lt $input.Count; $i++) { 134 | 135 | # PowerShell fun with input lines (if only one line it's a special case. sigh) 136 | If ( $input.Count -eq 1 ) { $vPodName = $input } 137 | Else { $vPodName = $input[$i] } 138 | 139 | # move vPod from _WorkInProgress to HOL-Staging in HOL-Dev 140 | Connect-CIServer $cloud -org $devOrgName -user $devUser -password $pass 141 | 142 | $wipCatalog = Get-Catalog $wipCatalogName 143 | $stageCatalog = Get-Catalog $stageCatalogName 144 | Try { 145 | $vpod = Get-CIVAppTemplate -Name $vPodName -ErrorAction 1 146 | } Catch { 147 | Write-Host "No such vApp Template $vPodName exists - skipping..." 148 | Continue 149 | } 150 | 151 | If ($vpod.length -gt 1 ) { 152 | Write-Host "More than one $vPodName vApp Template exists - skipping..." 153 | Continue 154 | } 155 | 156 | If ( $vpod.CustomizeOnInstantiate ) { 157 | Write-Host "$vpod is NOT set to make identical - skipping..." 158 | Continue 159 | } Else { 160 | Write-Host "$vpod is set to make identical and will be processed..." 161 | } 162 | 163 | # check each VM for suspended VMs. 164 | $VMs = Get-CIVMTemplate -VApp $vpod 165 | $skip = $False 166 | Foreach ($vm in $VMs) { 167 | If ($vm.Status -ne 'PoweredOff') { 168 | Write-Host "$vpod $vm.Name is not powered off" 169 | $skip = $True 170 | Continue 171 | } 172 | } 173 | 174 | If ( $skip ) { 175 | Write-Host "$vpod has a VM that is not powered off - skipping..." 176 | Continue 177 | } Else { 178 | Write-Host "$vpod VMs are all powered off. Ready to process..." 179 | } 180 | 181 | $description = $vpod.Description 182 | 183 | $catalogItem = $wipCatalog.ExtensionData.CatalogItems.CatalogItem | where { $_.Name -eq $vPodName } 184 | $ref = New-Object VMware.VimAutomation.Cloud.Views.Reference 185 | $ref.name = "this is a reference to the catalog Item we want to move" 186 | $ref.Href = $catalogItem.Href 187 | $ref.id = $catalogItem.id 188 | $ref.type = $catalogItem.type 189 | 190 | If ( $vpod.Catalog.Name -eq $wipCatalogName ) { 191 | 192 | Write-Host "Moving $vPodName from $wipCatalogName to $stageCatalogName..." 193 | $stageCatalog.ExtensionData.Move( $ref, $vPodName, $description ) 194 | 195 | } 196 | Disconnect-CIServer * -Confirm:$false 197 | 198 | # copy vPod from public HOL-Staging to HOL-Masters in WDC1 HOL 199 | Connect-CIServer $cloud -org $prodOrgName -user $prodUser -password $pass 200 | $stageCatalog = Get-Catalog $stageCatalogName 201 | $prodCatalog = Get-Catalog $prodCatalogName 202 | 203 | $catalogItem = $stageCatalog.ExtensionData.CatalogItems.CatalogItem | where { $_.Name -eq $vPodName } 204 | $ref = New-Object VMware.VimAutomation.Cloud.Views.Reference 205 | $ref.name = "this is a reference to the catalog Item we want to copy" 206 | $ref.Href = $catalogItem.Href 207 | $ref.id = $catalogItem.id 208 | $ref.type = $catalogItem.type 209 | 210 | Write-Host "Copying $vPodName from $stageCatalogName to $prodCatalogName..." 211 | $prodCatalog.ExtensionData.Copy( $ref, $vPodName, $description ) 212 | 213 | 214 | # shadow vPod in WDC1 215 | Write-Host "Shadowing $vPodName..." 216 | $ov = get-orgvdc *ut* | where { $_.enabled -eq "True" } 217 | $shadowList = @() 218 | try { 219 | $vPod = Get-CIVAppTemplate $vpodName -catalog $prodCatalogName -ErrorAction 1 220 | Add-CIVAppShadowsWait -o $ov -v $vPod -sleep 30 221 | Get-civapp $($vpodName + '_shadow_*') | % { 222 | if( $_.Status -ne "PoweredOff" ) { 223 | Write-Host -BackgroundColor Magenta -ForegroundColor Black "Bad Shadow:" $_.Name $_.Status 224 | } 225 | $shadowList += $_ 226 | } 227 | Write-Host "Removing shadows for $vPodName" 228 | $shadowList | Remove-civapp -Confirm:$false 229 | } 230 | catch { 231 | Write-Host -ForegroundColor Red "vPod $vPodName not found!" 232 | } 233 | Disconnect-ciserver * -Confirm:$false 234 | } 235 | 236 | ###END 237 | 238 | -------------------------------------------------------------------------------- /vPod-Mgmt/OvfAnalyzer.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .NOTES 3 | Name: OvfAnalyzer.ps1 4 | Author: Doug Baer 5 | Version: 1.2 6 | Date: 2015-07-01 7 | 8 | .SYNOPSIS 9 | Parse OVF files and extract sizing information for HOL vPod analysis 10 | 11 | .DESCRIPTION 12 | Obtain summary information per OVF: 13 | * vPod SKU (vApp name in OVF) 14 | * Count of VMs in the vApp 15 | * Total number of vCPUs requested 16 | * Total amount of RAM (GB) requested 17 | * Total amount of Disk (GB) requested 18 | 19 | Optionally report the same information per VM 20 | 21 | .PARAMETER 22 | Library - (required) Windows path to folder containing OVF(s). Script will traverse the tree looking for files with .OVF extension. 23 | 24 | .PARAMETER 25 | OutfilePath - (optional) Windows path to the CSV-formatted output file 26 | 27 | .PARAMETER 28 | ExpandVMs - (optional) switch to indicate whether summary (default) or per-VM data is reported 29 | 30 | .EXAMPLE 31 | OvfAnalyzer.ps1 -Library C:\OVFs 32 | 33 | .EXAMPLE 34 | OvfAnalyzer.ps1 -Library C:\OVFs -ExpandVMs 35 | 36 | .EXAMPLE 37 | OvfAnalyzer.ps1 -Library C:\OVFs -OutfilePath C:\temp\OVFs-Report.csv 38 | 39 | .EXAMPLE 40 | OvfAnalyzer.ps1 -Library C:\OVFs -OutfilePath C:\temp\OVFs-DetailReport.csv -ExpandVMs 41 | 42 | .CHANGELOG 43 | 1.0 - Initial concept 44 | 1.1 - Revised and cleaned up variables, added -ExpandVMs option 45 | 1.1.1 - Added documentation, renamed ContentLibrary parameter to Library, cleaned up code. 46 | 1.2 - Corrected per-VM reporting issue with disk space calculation 47 | 48 | #> 49 | 50 | [CmdletBinding()] 51 | param( 52 | [Parameter(Position=0,Mandatory=$true,HelpMessage="Path to the OVF Library", 53 | ValueFromPipeline=$False)] 54 | [System.String]$Library, 55 | 56 | [Switch]$ExpandVMs, 57 | 58 | [Parameter(Position=1,Mandatory=$false,HelpMessage="Path to the output file (CSV)", 59 | ValueFromPipeline=$False,ValueFromPipelineByPropertyName=$False)] 60 | [System.String]$OutfilePath 61 | 62 | ) 63 | 64 | ############################################################################### 65 | BEGIN { 66 | 67 | try { 68 | if( -not (Test-Path $Library) ) { 69 | Write-Host -Fore Red "Exiting: unable to find $Library" 70 | Exit 71 | } 72 | } 73 | catch { 74 | # 75 | } 76 | Write-Host -fore Green "`n=*=*=*=*=* OvfAnalyzer Begin $(Get-Date) *=*=*=*=*=" 77 | } 78 | 79 | ############################################################################### 80 | PROCESS { 81 | 82 | $report = @() 83 | $reportVMs = @() 84 | 85 | $libraryOvfs = @() 86 | Get-ChildItem $Library -recurse -include '*.ovf' | % { $libraryOvfs += $_.FullName } 87 | 88 | $totalOvfs = ($libraryOvfs | Measure-Object).Count 89 | $currentOvf = 0 90 | 91 | Foreach( $theOvf in $libraryOvfs ) { 92 | $currentOvf += 1 93 | Write-Host "Working on $currentOvf of $totalOvfs" 94 | 95 | [xml]$ovf = Get-Content $theOvf 96 | $totalVms = 0 97 | $totalVcpu = 0 98 | $totalGbRam = 0 99 | $totalGbDisk = 0 100 | 101 | $currentVpod = "" | Select SKU, NumVM, NumCPU, GbRAM, GbDisk 102 | $currentVpod.SKU = $ovf.Envelope.VirtualSystemCollection.Name 103 | 104 | $vpodVms = @() 105 | $vpodVms = $ovf.Envelope.VirtualSystemCollection.VirtualSystem 106 | 107 | Foreach( $vm in $vpodVms ) { 108 | $totalVms += 1 109 | $currentVM = "" | Select SKU, Name, NumCPU, GbRAM, GbDisk 110 | $currentVM.SKU = $ovf.Envelope.VirtualSystemCollection.Name 111 | $currentVM.Name = $vm.Name 112 | $currentVM.GbDisk = 0 113 | 114 | $numVcpu = ($vm.VirtualHardwareSection.Item | Where {$_.description -like 'Number of Virtual CPUs'}).VirtualQuantity 115 | $totalVcpu += $numVcpu 116 | $currentVM.NumCPU = $numVcpu 117 | 118 | $ramSize = ($vm.VirtualHardwareSection.Item | Where {$_.description -like 'Memory Size'}) 119 | 120 | Switch( $ramSize.AllocationUnits ) { 121 | 'byte * 2^20' { $GbRam = [int]($RamSize.VirtualQuantity) / 1024 } 122 | 'byte * 2^30' { $GbRam = [int]($RamSize.VirtualQuantity) } 123 | 'byte * 2^40' { $GbRam = 1024 * [int]($RamSize.VirtualQuantity) } 124 | default { $GbRam = 999999 } 125 | } 126 | 127 | $totalGbRam += $GbRam 128 | $currentVM.GbRAM = $GbRam 129 | 130 | $disks = ($vm.VirtualHardwareSection.Item | Where {$_.description -like "Hard disk*"} | Sort -Property AddressOnParent) 131 | $i = 0 132 | Foreach( $disk in $disks ) { 133 | $parentDisks = @($Disks) 134 | $diskName = $parentDisks[$i].ElementName 135 | $i++ 136 | $ref = ($disk.HostResource."#text") 137 | $ref = $ref.Remove(0,$ref.IndexOf("-") + 1) 138 | $thisDisk = $ovf.Envelope.DiskSection.disk | where { $_.diskId -match $ref } 139 | 140 | Switch( $thisDisk.capacityAllocationUnits ) { 141 | 'byte * 2^20' { $GbDisk ="{0:N2}" -f ([int]($thisDisk.capacity) / 1024) } 142 | 'byte * 2^30' { $GbDisk = [int]($thisDisk.capacity) } 143 | 'byte * 2^40' { $GbDisk = 1024 * [int]($thisDisk.capacity) } 144 | default { $diskSize = 99999999 } 145 | } 146 | 147 | $totalGbDisk += $GbDisk 148 | $currentVM.GbDisk += $GbDisk 149 | } 150 | $reportVMs += $currentVM 151 | } 152 | 153 | $currentVpod.NumVM = $totalVms 154 | $currentVpod.NumCPU = $totalVcpu 155 | $currentVpod.GbRAM = $totalGbRam 156 | $currentVpod.GbDisk = $totalGbDisk 157 | 158 | $report += $currentVpod 159 | } 160 | 161 | If ($outfilePath) { 162 | If( $ExpandVMs ) { 163 | $reportVMs | Export-Csv -UseCulture $OutfilePath 164 | } Else { 165 | $report | Export-Csv -UseCulture $OutfilePath 166 | } 167 | } Else { 168 | If( $ExpandVMs ) { 169 | $reportVMs | Sort -Property SKU | Format-Table -Autosize 170 | } Else { 171 | $report | Sort -Property SKU | Format-Table -Autosize 172 | } 173 | } 174 | 175 | } 176 | 177 | ############################################################################### 178 | 179 | END { 180 | Write-Host -fore Green "`n=*=*=*=*=* OvfAnalyzer Finished $(Get-Date) *=*=*=*=*=" 181 | } -------------------------------------------------------------------------------- /vPod-Mgmt/ReportDiskChains.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # ReportDiskChains.ps1 - v1.0.2 - 05 July 2017 3 | # 4 | #Report on disk chain lengths 5 | $cloudkey = 'HOL-DEV' 6 | $sysUser = "c_us04_vcore3_nee" 7 | $DevOrg = "us04-3-hol-dev-d" 8 | $WipCatalog = "_WorkInProgress" 9 | $ChainLengthWarn = 10 10 | 11 | Write-Host "/// HOL vCD Disk Chain Length Reporter ///" 12 | 13 | if( ($Global:DefaultCIServers).Length -gt 0 ) { 14 | Write-Host -ForegroundColor Yellow "Disconnecting from all clouds" 15 | Disconnect-CiServer * -Confirm:$false | Out-Null 16 | } 17 | 18 | #get system credential and log in as sysadmin (so we can see the chain lengths) 19 | $cloud = (Get-CloudInfoFromKey -Key $cloudkey)[0] 20 | $c = Get-Credential $sysUser 21 | Connect-CIserver $cloud -org system -Credential $c | Out-Null 22 | $c = $null 23 | 24 | Write-Host -ForegroundColor Green "`n/// Analyzing deployed vApps in $DevOrg at $(Get-Date)`n" 25 | 26 | get-org $DevOrg | get-civm "Main Console" | ? { 27 | $($_.ExtensionData.VCloudExtension.any.VirtualDisksMaxChainLength -as [int]) -gt $ChainLengthWarn } | select name, vapp, ` 28 | @{N='Owner id';E={$_.vapp.owner}}, ` 29 | @{N='Chain Length';E={$_.ExtensionData.VCloudExtension.any.VirtualDisksMaxChainLength} 30 | } | Sort-Object -Property "Chain Length" -Descending | ft -auto 31 | 32 | 33 | Write-Host -ForegroundColor Green "`n/// Analyzing templates in catalog $WipCatalog at $(Get-Date)`n" 34 | 35 | Get-CIVAppTemplate -Catalog $WipCatalog | Get-CIVMTemplate -Name "Main Console" | ? { 36 | $($_.ExtensionData.VCloudExtension.any.VirtualDisksMaxChainLength -as [int]) -gt $ChainLengthWarn } | select name, vapptemplate, ` 37 | @{N='Owner id';E={$_.vapptemplate.owner}}, ` 38 | @{N='Chain Length';E={$_.ExtensionData.VCloudExtension.any.VirtualDisksMaxChainLength} 39 | } | Sort-Object -Property "Chain Length" -Descending | ft -auto 40 | 41 | Write-Host "/// End $(Get-Date)" 42 | 43 | Disconnect-CiServer * -Confirm:$false 44 | -------------------------------------------------------------------------------- /vPod-Mgmt/VMDK-Hashing.psm1: -------------------------------------------------------------------------------- 1 | Function Get-VmdkHash { 2 | <# 3 | Create a list of MD5 hashes for the VMDKs at a given path 4 | Write the list to CHECKSUM-.txt at the root of the VPODPATH 5 | HashAlgorithm defaults to MD5, which is fine for file validation 6 | Requires Powershell 4.0 or higher 7 | #> 8 | PARAM( 9 | $VpodPath = $(throw "need -VPodName"), 10 | $SiteName = $(throw "need -SiteName"), 11 | $HashAlgorithm = 'MD5' #one of the supported types: MD5, SHA1, SHA256, SHA384, SHA512 12 | ) 13 | PROCESS { 14 | # See if the checksum file already exists. Don't do anything if it's already there. 15 | ## 16 | 17 | $vmdkHashes = @() 18 | Foreach ( $vmdk in (Get-ChildItem $VpodPath -Filter *.vmdk) ) { 19 | $vmdkHash = "" | Select FileName,Hash 20 | $vmdkHash.FileName = $vmdk.Name 21 | $vmdkHash.Hash = $(Get-FileHash $vmdk.FullName -Algorithm $HashAlgorithm).Hash 22 | $vmdkHashes += $vmdkHash 23 | Write-Host 24 | } 25 | $vmdkHashes | Export-Csv -NoTypeInformation $(Join-Path $VpodPath "CHECKSUMS-$SiteName.txt") 26 | } 27 | } #Get-VmdkHash 28 | 29 | Function Check-VmdkHash { 30 | <# 31 | Verify the VMDK hashes for Site A against the hashes in file from Site B 32 | Read CHECKSUM-.txt and CHECKSUM-.txt and output differences 33 | Does not care about which hashing algorithm was used as long as the same 34 | was used for both. 35 | #> 36 | PARAM( 37 | $VpodPath = $(throw "need -VPodPath"), 38 | $SiteName = $(throw "need -SiteName"), 39 | $SourceSiteName = $(throw "need -SourceSiteName") 40 | ) 41 | PROCESS { 42 | $good = $true 43 | # Import the CSV files into hash tables for comparison 44 | $sourceChecksumFile = $(Join-Path $VpodPath "CHECKSUMS-$SourceSiteName.txt") 45 | $sourceHashes = @{} 46 | Import-CSV $sourceChecksumFile | % { $sourceHashes.Add($_.FileName,$_.Hash) } 47 | 48 | $localChecksumFile = $(Join-Path $VpodPath "CHECKSUMS-$SiteName.txt") 49 | $localHashes = @{} 50 | Import-CSV $localChecksumFile | % { $localHashes.Add($_.FileName,$_.Hash) } 51 | 52 | Foreach ($FileName in ($localHashes.Keys)) { 53 | If( -Not( $sourceHashes[$FileName] -eq $localHashes[$FileName] ) ) { 54 | Write-Host -Fore Red "$FileName - hashes DO NOT match" 55 | $good = $false 56 | } 57 | $sourceHashes.Remove($FileName) 58 | } 59 | #Report 'extra' files in source that don't show up in localChecksumFile 60 | If( $sourceHashes.Length -ne 0 ) { 61 | Foreach ($FileName in ($sourceHashes.Keys)) { 62 | Write-Host -Fore Red "SOURCE FILE MISSING: $FileName" 63 | $good = $false 64 | } 65 | } 66 | If( -Not ($good) ) { 67 | Write-Host -Fore Red "Checksums for $VpodPath DO NOT match between $SourceSiteName and $SiteName" 68 | } Else { 69 | Write-Host -Fore Green "Checksums for $VpodPath match between $SourceSiteName and $SiteName" 70 | } 71 | return $good 72 | } 73 | } #Check-VmdkHash 74 | 75 | -------------------------------------------------------------------------------- /vPodChecker/vPodChecker-OLD.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | 3 | .SYNOPSIS This script is intended to run some standard config validation 4 | checks for Hands-on Labs vPods. It also attempts to remediate 5 | some simple and common misconfigurations like uuid.action and 6 | keyboard.typematicMinDelay on Linux vVMs as well as CPU and Memory 7 | reservations. 8 | 9 | .DESCRIPTION Check vPod configuration and remediate some misconfigurations 10 | 11 | .NOTES Requires PowerCLI -- tested with v6.0u1 12 | Version 1.01 - 24 February 2016 13 | Version 1.02 - 3 August 2016 14 | Version 1.03 - 16 February 2017 15 | Version 1.04 - 17 February 2017 16 | 17 | .EXAMPLE .\vPodChecker.ps1 18 | 19 | .INPUTS None 20 | 21 | .OUTPUTS Interactive: all output is written to console 22 | 23 | #> 24 | 25 | #####Check SSL certificates on PROVIDED links 26 | ## USER must provide this list based on what is in your pod 27 | ## Start by pulling this array from the labStartup file. We'll filter out non-https links 28 | $URLs = @{ 29 | 'https://vcsa-01a.corp.local/vsphere-client/' = 'vSphere Web Client' 30 | 'https://vcsa-01a.corp.local/ui/' = 'redirectIfNeeded' #HTML5 client 31 | #'http://stga-01a.corp.local/account/login' = 'FreeNAS' 32 | } 33 | 34 | $urlsToTest = $URLs.Keys | where { $_ -match 'https' } 35 | 36 | $DefaultSslPort = 443 37 | 38 | #FQDN(s) of vCenter server(s) 39 | $vCenters = @( 40 | 'vcsa-01a.corp.local' 41 | #'vcsa-02a.corp.local' 42 | ) 43 | 44 | # Credentials used to log in to all vCenters 45 | # (in vSphere 6, account with 'license administrator' privilege must be used) 46 | $vcuser = 'administrator@vsphere.local' 47 | $password = 'VMware1!' 48 | 49 | $sleepSeconds = 10 50 | 51 | # calculating ending year based on today's date 52 | $a = Get-Date 53 | If ( $a.Month -lt 6 ) { # Spring Release dev cycle (Jan to May) 54 | $minYear = $a.Year # end of this year 55 | } Else { # VMworld dev cycle so end of next year 56 | $minYear = $a.Year + 1 57 | } 58 | $maxYear = $minYear + 1 59 | # HOL licenses should NOT expire before this date 60 | $chkDateMin = Get-Date "01/01/$minYear 12:00:00 AM" 61 | Write-Host "HOL licenses should NOT expire before $chkDateMin" 62 | 63 | # HOL licenses should expire before this date 64 | $chkDateMax = Get-Date "01/28/$maxYear 12:00:00 AM" 65 | Write-Host "HOL licenses should expire before $chkDateMax" 66 | 67 | $input = Read-Host -Prompt "If these dates are acceptable, press a key to continue" 68 | 69 | $licensePass = $true 70 | 71 | #must be defined in order to pass as reference for looping during connect 72 | $result = '' 73 | 74 | #Certificate Validation 75 | $minValidDate = [datetime]"12/31/$minYear" 76 | 77 | Write-Host "HOL SSL Certificates should NOT expire before $minValidDate" 78 | 79 | 80 | ############################################################################## 81 | 82 | Try { 83 | #For PowerCLI v6.x 84 | #$PowerCliInit = 'C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1' 85 | #For PowerCLI v6.5 86 | $PowerCliInit = 'C:\Program Files (x86)\VMware\Infrastructure\PowerCLI\Scripts\Initialize-PowerCLIEnvironment.ps1' 87 | . $PowerCliInit $true 88 | } 89 | Catch { 90 | Write-Host "No PowerCLI found, unable to continue." 91 | Write-VpodProgress "FAIL - No PowerCLI" 'FAIL-1' 92 | Break 93 | } 94 | 95 | #Disable SSL certificate validation checks... it's a Lab! 96 | $scvc = [System.Net.ServicePointManager]::ServerCertificateValidationCallback 97 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true} 98 | 99 | ############################################################################## 100 | 101 | Function Connect-VC ([string]$server, [string]$username, [string]$password, [REF]$result) { 102 | <# 103 | This function attempts once to connect to the specified vCenter 104 | It sets the $result variable to 'success' or 'fail' based on the result 105 | #> 106 | Try { 107 | Connect-ViServer -server $server -username $username -password $password -ErrorAction 1 | Out-Null 108 | Write-Host "Connection Successful" 109 | $result.value = "success" 110 | } 111 | Catch { 112 | Write-Host "Failed to connect to server $server" 113 | Write-Host $_.Exception.Message 114 | $result.value = $false 115 | } 116 | } #End Connect-VC 117 | 118 | 119 | Function Test-TcpPort ([string]$server, [int]$port) { 120 | Try { 121 | $socket = New-Object Net.Sockets.TcpClient 122 | $socket.Connect($server,$port) 123 | if($socket.Connected) { 124 | Write-Host "Successfully connected to server $server on port $port" 125 | return $true 126 | } 127 | } 128 | Catch { 129 | Write-Host "Failed to connect to server $server on port $port" 130 | Return $false 131 | } 132 | $socket.Dispose() 133 | } #End Test-TcpPort 134 | 135 | 136 | ############################################################################## 137 | ##### Check and report SSL Certificates 138 | ############################################################################## 139 | Write-Host "==== SSL CERTIFICATES ====" 140 | 141 | $ID=0 142 | Write-Host "# Host/Port IssuedTo Expires (Remaining) Issuer" 143 | 144 | foreach( $url in $urlsToTest ) { 145 | $url = $url.ToLower() 146 | if( $url -like "https*" ) { 147 | Write-Verbose "HTTPS url found: $url" 148 | $h = [regex]::Replace($url, "https://([a-z\.0-9\-]+).*", '$1') 149 | if( ($url.Split(':') | Measure-Object).Count -gt 2 ) { 150 | $p = [regex]::Replace($url, "https://[a-z\.0-9\-]+\:(\d+).*", '$1') 151 | } else { 152 | $p = $DefaultSslPort 153 | } 154 | Write-Verbose "Checking $h on port $p" 155 | 156 | $ID+=1 157 | try { 158 | $HostConnection = New-Object System.Net.Sockets.TcpClient($h,$p) 159 | try { 160 | $Stream = New-Object System.Net.Security.SslStream($HostConnection.GetStream(),$false, { 161 | param($sender, $certificate, $chain, $sslPolicyErrors) 162 | return $true 163 | }) 164 | $Stream.AuthenticateAsClient($h) 165 | 166 | $sslCertificate = $Stream.Get_RemoteCertificate() 167 | $CN=(($sslCertificate.Subject -split "CN=")[1] -split ",")[0] 168 | $Issuer=$sslCertificate.Issuer 169 | $validTo = [datetime]::Parse($sslCertificate.GetExpirationDatestring()) 170 | 171 | $validTime = New-Timespan -Start $minValidDate -End $ValidTo 172 | If( $validTime.Days -lt 0 ) { 173 | #To distinguish from "Error Red" 174 | $MyFontColor="DarkRed" 175 | } Else { 176 | $MyFontColor="DarkGreen" 177 | } 178 | $validDays = $validTime.Days 179 | 180 | Write-Host "$ID $h $p`t$CN`t$validTo ($validDays)`t$Issuer" -ForegroundColor $MyFontColor 181 | } 182 | 183 | catch { throw $_ } 184 | finally { $HostConnection.close() } 185 | } 186 | 187 | catch { 188 | #Write-Host "$ID $WebsiteURL " $_.exception.innerexception.message -ForegroundColor red 189 | #unroll the exception 190 | $e = $_.Exception 191 | $msg = $e.Message 192 | while ($e.InnerException) { 193 | $e = $e.InnerException 194 | $msg += ">" + $e.Message 195 | } 196 | Write-Host "$ID $h " $msg -ForegroundColor Red 197 | Write-Host "*** Please manually check SSL certificate on $h ***" -ForegroundColor DarkRed 198 | } 199 | } 200 | } 201 | Write-Host "==========================" 202 | 203 | ############################################################################## 204 | ##### Test each vCenter's resources 205 | ############################################################################## 206 | 207 | Foreach ($vcserver in $vCenters) { 208 | Write-Host "$(Get-Date) Connecting to $vcserver ..." -ForegroundColor DarkGreen 209 | Do { 210 | Connect-VC $vcserver $vcuser $password ([REF]$result) 211 | Start-Sleep $sleepSeconds 212 | } Until ($result -eq "success") 213 | 214 | ############################################################################## 215 | ##### Check Host Settings 216 | ############################################################################## 217 | Write-Host "==== HOST CONFIGURATION - NTP ====" 218 | $hostReport = @() 219 | 220 | ##### NTP is configured 221 | 222 | $allhosts = Get-VMHost 223 | foreach ($h in $allhosts) { 224 | $row = "" | Select HOSTNAME, NTPDRUNNING, NTPDPOLICY, NTPSERVER 225 | $row.HOSTNAME = $h.name 226 | $ntpData = Get-VMHostService -VMHost $h | where { $_.key -eq 'ntpd' } 227 | $row.NTPDRUNNING = $ntpData.Running 228 | $row.NTPDPOLICY = $ntpData.Policy 229 | $row.NTPSERVER = Get-VMHostNtpServer -VMHost $h 230 | $hostReport += $row 231 | } 232 | $hostReport | ft -auto 233 | If ( $row) { Remove-Variable row } 234 | Write-Host "==========================" 235 | 236 | 237 | ############################################################################## 238 | ##### Check vVM Settings 239 | ############################################################################## 240 | Write-Host "==== L2 VM CONFIGURATION ====" 241 | $vmReport = @() 242 | 243 | ##### Report/correct UUID.action setting on vVMs 244 | 245 | $allvms = Get-VM 246 | foreach ($vm in $allvms) { 247 | $row = "" | Select VMNAME,OSTYPE,UUIDACTION,TYPEDELAY 248 | $row.VMNAME = $vm.name 249 | 250 | $currentUuidAction = Get-AdvancedSetting -en $vm -name uuid.action 251 | $currentUuidActionValue = $currentUuidAction.Value 252 | if( $currentUuidActionValue -eq "keep" ) { 253 | $row.UUIDACTION = $currentUuidActionValue 254 | } elseif(! $currentUuidActionValue ) { 255 | try { 256 | New-AdvancedSetting -en $vm -name uuid.action -value 'keep' -Confirm:$false -ErrorAction 1 | Out-Null 257 | $row.UUIDACTION = "was BLANK" 258 | } catch { 259 | Write-Host -Fore Red "Failed to create UUID.action on $($vm.name)" 260 | $row.UUIDACTION = "FIXMANUAL" 261 | } 262 | } else { 263 | try { 264 | Set-AdvancedSetting $currentUuidAction -value 'keep' -Confirm:$false -ErrorAction 1 265 | $row.UUIDACTION = "was $currentUuidActionValue" 266 | } catch { 267 | Write-Host -Fore Red "Failed to set UUID.action on $($vm.name)" 268 | Write-Host -Fore Red " value remains: $((Get-AdvancedSetting -en $vm -name uuid.action).value)" 269 | $row.UUIDACTION = "FIXMANUAL" 270 | } 271 | } 272 | 273 | ##### Report/correct typematic delay... for Linux machines only 274 | 275 | $row.OSTYPE = $vm.GuestId 276 | if( $vm.GuestId -match 'linux|ubuntu|debian|centos|sles|redhat|photon|other' ) { 277 | $currentTypeDelay = Get-AdvancedSetting -en $vm -name keyboard.typematicMinDelay 278 | $currentTypeDelayValue = $currentTypeDelay.Value 279 | if( $currentTypeDelayValue -eq 2000000 ) { 280 | $row.TYPEDELAY = $currentTypeDelayValue 281 | } elseif(! $currentTypeDelay ) { 282 | try { 283 | New-AdvancedSetting -en $vm -name keyboard.typematicMinDelay -value 2000000 -Confirm:$false -ErrorAction 1 | Out-Null 284 | $row.TYPEDELAY = "was BLANK" 285 | } catch { 286 | Write-Host -Fore Red "Failed to create keyboard.typematicMinDelay on $($vm.name)" 287 | $row.TYPEDELAY = "FIXMANUAL" 288 | } 289 | 290 | } else { 291 | try { 292 | Set-AdvancedSetting $currentTypeDelay -value 2000000 -Confirm:$false -ErrorAction 1 293 | $row.TYPEDELAY = "was $currentTypeDelayValue" 294 | } catch { 295 | Write-Host -Fore Red "Failed to set keyboard.typematicMinDelay on $($vm.name)" 296 | Write-Host -Fore Red " value remains: $((Get-AdvancedSetting -en $vm -name keyboard.typematicMinDelay).value)" 297 | $row.TYPEDELAY = "FIXMANUAL" 298 | } 299 | } 300 | } 301 | $vmReport += $row 302 | } 303 | 304 | $vmReport | ft 305 | If ( $row) { Remove-Variable row } 306 | Write-Host "==========================" 307 | 308 | ############################################################################## 309 | ##### Check vVM Resource Settings 310 | ############################################################################## 311 | Write-Host "==== L2 RESOURCE CONFIGURATION ====" 312 | Write-Host "" 313 | $vmReport = @() 314 | 315 | foreach ($vm in $allvms) { 316 | $row = "" | Select VMNAME,CpuReservationMhz,MemReservationGB,CpuSharesLevel,MemSharesLevel 317 | $row.VMNAME = $vm.name 318 | $row.CpuReservationMhz = $vm.VMResourceConfiguration.CpuReservationMhz 319 | $row.MemReservationGB = $vm.VMResourceConfiguration.MemReservationGB 320 | $row.CpuSharesLevel = $vm.VMResourceConfiguration.CpuSharesLevel 321 | $row.MemSharesLevel = $vm.VMResourceConfiguration.MemSharesLevel 322 | 323 | If ( $vm.VMResourceConfiguration.CpuReservationMhz ) { 324 | Write-Host "Setting " $vm.Name " CPU reservation to 0 per HOL standards..." -foregroundcolor "red" 325 | $vm | Get-VMResourceConfiguration | Set-VMResourceConfiguration -CPUReservationMhz 0 326 | $row.CpuReservationMhz = "was " + $vm.VMResourceConfiguration.CpuReservationMhz 327 | } 328 | If ( $vm.VMResourceConfiguration.MemReservationGB ) { 329 | Write-Host "Setting " $vm.Name " memory reservation to 0 per HOL standards..." -foregroundcolor "red" 330 | $vm | Get-VMResourceConfiguration | Set-VMResourceConfiguration -MemReservationMB 0 331 | $row.MemReservationGB = "was " + $vm.VMResourceConfiguration.MemReservationGB 332 | } 333 | If ( $vm.VMResourceConfiguration.CpuSharesLevel -ne "Normal" ) { 334 | #Write-Host "Notification only: " $vm.Name " CPU Shares are: " $vm.VMResourceConfiguration.CpuSharesLevel 335 | } 336 | If ( $vm.VMResourceConfiguration.MemSharesLevel -ne "Normal" ) { 337 | #Write-Host "Notification only: " $vm.Name " CPU Shares are: " $vm.VMResourceConfiguration.MemSharesLevel 338 | } 339 | $vmReport += $row 340 | } 341 | 342 | $vmReport | ft 343 | If ( $row) { Remove-Variable row } 344 | Write-Host "==========================" 345 | 346 | ############################################################################## 347 | ##### Check Licensing 348 | ############################################################################## 349 | 350 | Write-Host "==== VCENTER LICENSES ====" 351 | $licenseReport = @() 352 | 353 | #check for evaluation licenses in use 354 | $LM = Get-View LicenseManager 355 | $LAM = Get-View $LM.LicenseAssignmentManager 356 | $param = @($null) 357 | $assets = $LAM.GetType().GetMethod("QueryAssignedLicenses").Invoke($LAM,$param) 358 | 359 | 360 | foreach ($asset in $assets) { 361 | if ( $asset.AssignedLicense.LicenseKey -eq '00000-00000-00000-00000-00000' ) { 362 | # special case - make certain nothing is in evaluation mode 363 | $name = $asset | Select-Object -ExpandProperty EntityDisplayName 364 | Write-Host "Please check EVALUATION assignment on $name!" -foregroundcolor "red" 365 | $licensePass = $false 366 | } 367 | } 368 | 369 | # query the license expiration for all installed licenses 370 | 371 | foreach( $License in ($LM | Select -ExpandProperty Licenses) ) { 372 | if ( !($License.LicenseKey -eq '00000-00000-00000-00000-00000') ) { 373 | $row = "" | Select LICENSENAME,LICENSEKEY,EXPIRATION,STATUS 374 | $VC = ([Uri]$LM.Client.ServiceUrl).Host 375 | $Name = $License.Name 376 | $lKey = $License.LicenseKey 377 | $used = $License.Used 378 | $labels = $License.Labels | Select -ExpandProperty Value 379 | 380 | $row.LICENSENAME = $Name 381 | $row.LICENSEKEY = $lKey 382 | 383 | $expDate = $License.Properties | Where-Object {$_.Key -eq "expirationDate"} | Select-Object -ExpandProperty Value 384 | 385 | if( $expDate ) { 386 | $row.EXPIRATION = $expDate 387 | } else { 388 | $row.EXPIRATION = 'NEVER' 389 | } 390 | 391 | if( $expDate -and (($expDate -ge $chkDateMin) -and ($expDate -le $chkDateMax)) ) { 392 | #Write-Verbose "License $Name $lKey is good and expires $expDate" 393 | $row.STATUS = 'GOOD' 394 | if( $used -eq 0 ) { 395 | Write-Host "License $Name is UNASSIGNED and should be removed." -foregroundcolor DarkRed 396 | $row.STATUS = 'UNASSIGNED' 397 | $licensePass = $false 398 | } 399 | } else { 400 | if( ! $expDate ) { 401 | Write-Host "License $Name $lKey NEVER expires!!" -foregroundcolor Red 402 | } else { 403 | Write-Host "License $Name $lKey is BAD. It expires $expDate" -ForegroundColor Red 404 | $row.STATUS = 'EXPIRING' 405 | } 406 | $licensePass = $false 407 | } 408 | # need to make certain expDate is AFTER chkDate 409 | } 410 | $licenseReport += $row 411 | } 412 | 413 | $licenseReport | ft 414 | 415 | if( $licensePass ) { 416 | Write-Host "Well done! Final result of $vcserver license check is PASS" -foregroundcolor "green" 417 | } 418 | else { 419 | Write-Host "Final result of $vcserver license check is FAIL" -foregroundcolor "red" 420 | } 421 | Remove-Variable row 422 | Write-Host "==========================" 423 | 424 | ##### Disconnect from current vCenter 425 | Write-Host "$(Get-Date) disconnecting from $vcserver ..." -ForegroundColor DarkGreen 426 | Disconnect-VIServer -Server $vcserver -Confirm:$false 427 | 428 | } # for each vCenter 429 | 430 | ###### END ###### 431 | --------------------------------------------------------------------------------