This commit is contained in:
Zack Meier
2026-04-15 15:45:50 -05:00
commit 1d304511b8
613 changed files with 140998 additions and 0 deletions
@@ -0,0 +1 @@
# It all starts with a single line of powershell code.
@@ -0,0 +1,131 @@
<#
.SYNOPSIS
A short one-line action-based description, e.g. 'Tests if a function is valid'
.DESCRIPTION
A longer description of the function, its purpose, common use cases, etc.
.NOTES
Information or caveats about the function e.g. 'This function is not supported in Linux'
.LINK
Specify a URI to a help page, this will show when Get-Help -Online is used.
.EXAMPLE
Test-MyTestFunction -Verbose
Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
#>
<# Scheduled Task metadata
General
Get IPs for PA
run as ndgov\!itdvcenterppa
run whether user is logged on or not
Triggers
Daily, 11am
Daily, 11pm
Actions
old-C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noninteractive -file "C:\itdscript\GetIPs.ps1"
new-"C:\Program Files\PowerShell\7\pwsh.exe" -noninteractive -file "F:\GetVMwareVMGuestIPsForPA\GetVMwareVMGuestIPsForPA.ps1"
Settings
allow task to be run on demand
stop the task if it runs longer than 1 hour -eq $true
if the running task does not end when requested, force it to stop
#>
$TimeStamp = Get-Date -UFormat "%Y%m%d%H%M%S"
#Connect
Connect-ITDvCenter -Credential $Secret:ndgov_svcitdvmvcauto
##Windows
#Output File
$OutFileWin = "c:\inetpub\wwwroot\Win.txt"
#$Date = Get-Date -UFormat "%Y%m%d%H%M%S"
Get-Item -Path $OutFileWin | Copy-Item -Destination "F:\GetVMwareVMGuestIPsForPA\Backup\Win\$Timestamp-Win.txt"
Remove-Item $OutFileWin
Start-Sleep -Seconds 5
#Get Powered On VM's
$vmwin = get-VM | Where-Object { $_.PowerState -eq "PoweredOn" `
-and ($_.GuestID -eq "windows7Guest" `
-or $_.GuestID -eq "windows7_64Guest" `
-or $_.GuestID -eq "windows7Server64Guest" `
-or $_.GuestID -eq "windows8_64Guest" `
-or $_.GuestID -eq "windows8Server64Guest" `
-or $_.GuestID -eq "windows9Server64Guest" `
-or $_.GuestID -eq "winLonghorn64Guest" `
-or $_.GuestID -eq "winLonghornGuest" `
-or $_.GuestID -eq "winNetStandardGuest" `
-or $_.GuestID -eq "winNetEnterpriseGuest" `
-or $_.GuestID -eq "windows9_64Guest" `
-or $_.GuestID -eq "windows2019srv_64Guest" `
-or $_.GuestID -eq "windows2019srvNext_64Guest") }
$vmviewwin = $vmwin | Get-View
$Outputwin = ""
#Loop through VM's, NIC's, and IP addresses.
Foreach ($v in $vmviewwin) {
Foreach ($nic in $v.Guest.Net) {
Foreach ($IP in $nic.IPAddress) {
If ($IP -notlike "fe80*" -and $IP -notlike "192.168.*" -and $IP -notlike "172.16*") {
$OutputWin += $IP + "`n"
}
}
}
}
#If ($Outputwin -ne "") {$OutputWin | Out-File $OutFileWin -Encoding utf8 -NoNewline}
If ($Outputwin -ne "") { $OutputWin | Out-File $OutFileWin -Encoding ASCII -NoNewline }
##Linux
#Output File
$OutFileLin = "c:\inetpub\wwwroot\Lin.txt"
$Date = Get-Date -UFormat "%Y%m%d%H%M%S"
Get-Item -Path $OutFileLin | Copy-Item -Destination "F:\GetVMwareVMGuestIPsForPA\Backup\Lin\$Timestamp-Lin.txt"
Remove-Item $OutFileLin
Start-Sleep -Seconds 5
#Get Powered On VM's
$vmLin = get-VM | Where-Object { $_.PowerState -eq "PoweredOn" `
-and ($_.GuestID -eq "centos6_64Guest" `
-or $_.GuestID -eq "centos64Guest" `
-or $_.GuestID -eq "centos7_64Guest" `
-or $_.GuestID -eq "oracleLinux64Guest" `
-or $_.GuestID -eq "oracleLinux7_64Guest" `
-or $_.GuestID -eq "rhel4Guest" `
-or $_.GuestID -eq "rhel5Guest" `
-or $_.GuestID -eq "rhel5_64Guest" `
-or $_.GuestID -eq "rhel6Guest" `
-or $_.GuestID -eq "rhel6_64Guest" `
-or $_.GuestID -eq "rhel7_64Guest" `
-or $_.GuestID -eq "rhel8_64Guest" `
-or $_.GuestID -eq "rhel9_64Guest" `
-or $_.GuestID -eq "sles11_64Guest" `
-or $_.GuestID -eq "sles12_64Guest" `
-or $_.GuestID -eq "ubuntu64Guest") }
$vmviewlin = $vmLin | Get-View
$OutputLin = ""
#Loop through VM's, NIC's, and IP addresses.
Foreach ($v in $vmviewlin) {
Foreach ($nic in $v.Guest.Net) {
Foreach ($IP in $nic.IPAddress) {
If ($IP -notlike "fe80*" -and $IP -notlike "192.168.*" -and $IP -notlike "172.16*") {
$OutputLin += $IP + "`n"
}
}
}
}
#If ($OutputLin -ne "") {$OutputLin | Out-File $OutFileLin -Encoding utf8 -NoNewline}
If ($OutputLin -ne "") { $OutputLin | Out-File $OutFileLin -Encoding ASCII -NoNewline }
Disconnect-ITDvCenter
@@ -0,0 +1,270 @@
<#
.SYNOPSIS
Daily HPE OneView enclosure and server inventory for PowerBI trending and lifecycle planning.
.DESCRIPTION
Iterates across all configured HPE OneView appliances, collecting enclosure and server
hardware metadata including compute capacity, power state, and server profile assignment.
Exports timestamped CSVs and inserts into SQL each run.
Append daily runs to build a historical dataset for PowerBI trend analysis and
physical hardware purchasing / lifecycle decisions.
Appliances processed:
itdoneviewp1.nd.gov - Rack servers only (no enclosures)
itdbissyncompp1.nd.gov - Synergy enclosures + servers
itdmdnsyncompp1.nd.gov - Synergy enclosures + servers
.NOTES
Run the following DDL once to create the destination tables:
DROP TABLE IF EXISTS [dbo].[VMware_Trends_Enclosure]
CREATE TABLE [dbo].[VMware_Trends_Enclosure] (
[ReportDate] DATETIME2 NOT NULL,
[ApplianceConnection] NVARCHAR(100) NULL,
[EnclosureName] NVARCHAR(255) NOT NULL,
[EnclosureModel] NVARCHAR(255) NULL,
[EnclosureSerialNumber] NVARCHAR(100) NULL,
[Status] NVARCHAR(50) NULL,
[DeviceBayCount] INT NULL
)
DROP TABLE IF EXISTS [dbo].[VMware_Trends_Server]
CREATE TABLE [dbo].[VMware_Trends_Server] (
[ReportDate] DATETIME2 NOT NULL,
[ApplianceConnection] NVARCHAR(100) NULL,
[EnclosureName] NVARCHAR(255) NULL,
[BayNumber] INT NULL,
[ServerHardwareName] NVARCHAR(255) NOT NULL,
[ServerName] NVARCHAR(255) NULL,
[ServerModel] NVARCHAR(255) NULL,
[ServerSerialNumber] NVARCHAR(100) NULL,
[Status] NVARCHAR(50) NULL,
[PowerState] NVARCHAR(50) NULL,
[ServerProfileName] NVARCHAR(255) NULL,
[ProcessorType] NVARCHAR(255) NULL,
[ProcessorCount] INT NULL,
[MemoryGB] DECIMAL(10,2) NULL,
[FormFactor] NVARCHAR(100) NULL
)
EnclosureName and BayNumber are NULL for rack-mounted servers not seated in an enclosure.
ServerName is the iLO/DNS hostname populated once a server profile is assigned.
#>
[CmdletBinding()]
param(
)
#region --- Setup ---------------------------------------------------------------
[string[]] $OVHostnames = @(
'itdoneviewp1.nd.gov',
'itdbissyncompp1.nd.gov',
'itdmdnsyncompp1.nd.gov'
)
[string] $OutputPath = 'C:\temp\OV_Trends\'
[string] $ServerInstance = 'itdintsql22p1.nd.gov\INTSQL22P1'
[string] $Database = 'ITD-Systems-Automation'
[string] $EnclosureTable = 'VMware_Trends_Enclosure'
[string] $ServerTable = 'VMware_Trends_Server'
[System.Management.Automation.PSCredential] $OVCredential = $Secret:ndgov_svcitdvmhpe
[System.Management.Automation.PSCredential] $SqlCredential = $Secret:sql_itdpsu1
$RunDate = Get-Date
$DateStamp = $RunDate.ToString('yyyyMMdd')
$Timestamp = $RunDate.ToString('yyyy-MM-dd')
if (-not (Test-Path -Path $OutputPath)) {
New-Item -ItemType Directory -Path $OutputPath | Out-Null
}
Start-Transcript -Path (Join-Path $OutputPath "OVServerInventory_$DateStamp.log") -Append
#endregion
#region --- Collect Data From All Appliances -----------------------------------
$AllEnclosureResults = [System.Collections.Generic.List[PSCustomObject]]::new()
$AllServerResults = [System.Collections.Generic.List[PSCustomObject]]::new()
foreach ($OVHostname in $OVHostnames) {
Write-Verbose "Connecting to HPE OneView: $OVHostname"
Connect-OVMgmt -Hostname $OVHostname -Credential $OVCredential -AuthLoginDomain nd.gov -LoginAcknowledge
#--- Enclosures ------------------------------------------------------------
Write-Verbose "[$OVHostname] Gathering enclosures..."
$Enclosures = Get-OVEnclosure
# Build URI map for server lookups (only populated if there are enclosures)
$EnclosureUriMap = @{}
foreach ($Enclosure in $Enclosures) {
$EnclosureUriMap[$Enclosure.uri] = $Enclosure.name
$AllEnclosureResults.Add([PSCustomObject]@{
ReportDate = $Timestamp
ApplianceConnection = $OVHostname
EnclosureName = $Enclosure.name
EnclosureModel = $Enclosure.model
EnclosureSerialNumber = $Enclosure.serialNumber
Status = $Enclosure.status
DeviceBayCount = ($Enclosure.deviceBays | Measure-Object).Count
})
}
#--- Servers ---------------------------------------------------------------
Write-Verbose "[$OVHostname] Gathering servers..."
$Servers = Get-OVServer | Where-Object { $_.serverName -like 'itdvm*' }
# Build profile URI map for this appliance
$ProfileUriMap = @{}
Get-OVServerProfile | ForEach-Object {
$ProfileUriMap[$_.uri] = $_.name
}
Write-Verbose "[$OVHostname] Processing $($Servers.Count) servers..."
foreach ($Server in $Servers) {
$EnclosureName = if ($Server.locationUri) {
$EnclosureUriMap[$Server.locationUri]
} else {
$null
}
$ProfileName = if ($Server.serverProfileUri) {
$ProfileUriMap[$Server.serverProfileUri]
} else {
$null
}
$MemoryGB = if ($null -ne $Server.memoryMb -and $Server.memoryMb -gt 0) {
[Math]::Round($Server.memoryMb / 1024, 2)
} else {
$null
}
$AllServerResults.Add([PSCustomObject]@{
ReportDate = $Timestamp
ApplianceConnection = $OVHostname
EnclosureName = $EnclosureName
BayNumber = if ($null -ne $Server.position) { [int]$Server.position } else { $null }
ServerHardwareName = $Server.name
ServerName = $Server.serverName
ServerModel = $Server.model
ServerSerialNumber = $Server.serialNumber
Status = $Server.status
PowerState = $Server.powerState
ServerProfileName = $ProfileName
ProcessorType = $Server.processorType
ProcessorCount = if ($null -ne $Server.processorCount) { [int]$Server.processorCount } else { $null }
MemoryGB = $MemoryGB
FormFactor = $Server.formFactor
})
}
Disconnect-OVMgmt
Write-Verbose "[$OVHostname] Disconnected."
}
#endregion
#region --- Export CSVs --------------------------------------------------------
$EnclosureCsv = Join-Path $OutputPath "OVEnclosures_$DateStamp.csv"
$ServerCsv = Join-Path $OutputPath "OVServers_$DateStamp.csv"
$AllEnclosureResults | Export-Csv -Path $EnclosureCsv -NoTypeInformation
$AllServerResults | Export-Csv -Path $ServerCsv -NoTypeInformation
Write-Verbose "Exported $($AllEnclosureResults.Count) enclosure records to: $EnclosureCsv"
Write-Verbose "Exported $($AllServerResults.Count) server records to: $ServerCsv"
#endregion
#region --- SQL Insert: Enclosures ---------------------------------------------
$EnclosureTable_DT = [System.Data.DataTable]::new()
$EnclosureColDefs = [ordered]@{
ReportDate = [datetime]
ApplianceConnection = [string]
EnclosureName = [string]
EnclosureModel = [string]
EnclosureSerialNumber = [string]
Status = [string]
DeviceBayCount = [int]
}
foreach ($Col in $EnclosureColDefs.GetEnumerator()) {
$Column = [System.Data.DataColumn]::new($Col.Key, $Col.Value)
$Column.AllowDBNull = $true
[void]$EnclosureTable_DT.Columns.Add($Column)
}
foreach ($Row in $AllEnclosureResults) {
$DataRow = $EnclosureTable_DT.NewRow()
foreach ($Col in $EnclosureColDefs.Keys) {
$Val = $Row.$Col
$DataRow[$Col] = if ($null -ne $Val) { $Val } else { [DBNull]::Value }
}
[void]$EnclosureTable_DT.Rows.Add($DataRow)
}
Write-SqlTableData -ServerInstance $ServerInstance -DatabaseName $Database -SchemaName 'dbo' `
-TableName $EnclosureTable -Credential $SqlCredential -InputData $EnclosureTable_DT
Write-Verbose "Inserted $($EnclosureTable_DT.Rows.Count) enclosure records into [$Database].[dbo].[$EnclosureTable]"
#endregion
#region --- SQL Insert: Servers ------------------------------------------------
$ServerTable_DT = [System.Data.DataTable]::new()
$ServerColDefs = [ordered]@{
ReportDate = [datetime]
ApplianceConnection = [string]
EnclosureName = [string]
BayNumber = [int]
ServerHardwareName = [string]
ServerName = [string]
ServerModel = [string]
ServerSerialNumber = [string]
Status = [string]
PowerState = [string]
ServerProfileName = [string]
ProcessorType = [string]
ProcessorCount = [int]
MemoryGB = [decimal]
FormFactor = [string]
}
foreach ($Col in $ServerColDefs.GetEnumerator()) {
$Column = [System.Data.DataColumn]::new($Col.Key, $Col.Value)
$Column.AllowDBNull = $true
[void]$ServerTable_DT.Columns.Add($Column)
}
foreach ($Row in $AllServerResults) {
$DataRow = $ServerTable_DT.NewRow()
foreach ($Col in $ServerColDefs.Keys) {
$Val = $Row.$Col
$DataRow[$Col] = if ($null -ne $Val) { $Val } else { [DBNull]::Value }
}
[void]$ServerTable_DT.Rows.Add($DataRow)
}
Write-SqlTableData -ServerInstance $ServerInstance -DatabaseName $Database -SchemaName 'dbo' `
-TableName $ServerTable -Credential $SqlCredential -InputData $ServerTable_DT
Write-Verbose "Inserted $($ServerTable_DT.Rows.Count) server records into [$Database].[dbo].[$ServerTable]"
#endregion
#region --- Cleanup ------------------------------------------------------------
Stop-Transcript
#endregion
@@ -0,0 +1,192 @@
<#
.SYNOPSIS
Daily VMware Cluster metadata report for PowerBI trending and capacity planning.
.DESCRIPTION
Collects cluster-level metadata from vCenter including DRS/HA configuration,
aggregate compute capacity, and effective resource availability.
Exports a timestamped CSV and inserts into SQL each run.
Append daily runs to build a historical dataset for PowerBI trend analysis.
.NOTES
Run the following DDL once to create the destination table:
DROP TABLE IF EXISTS [dbo].[VMware_Trends_Cluster]
CREATE TABLE [dbo].[VMware_Trends_Cluster] (
[ReportDate] DATETIME2 NOT NULL,
[ClusterName] NVARCHAR(255) NOT NULL,
[Datacenter] NVARCHAR(100) NULL,
[DrsEnabled] BIT NOT NULL,
[DrsMode] NVARCHAR(50) NULL,
[HaEnabled] BIT NOT NULL,
[HostCount] INT NULL,
[EffectiveHostCount] INT NULL,
[TotalCpuCores] INT NULL,
[TotalCpuMhz] INT NULL,
[EffectiveCpuMhz] INT NULL,
[TotalMemoryGB] DECIMAL(10,2) NULL,
[EffectiveMemoryGB] DECIMAL(10,2) NULL,
[UsedMemoryGB] DECIMAL(10,2) NULL,
[VMCount] INT NULL
)
TotalCpuMhz, EffectiveCpuMhz, TotalMemoryGB, and EffectiveMemoryGB come from
vCenter's cluster summary (real-time aggregate).
UsedMemoryGB is summed from each host's current MemoryUsageGB.
TotalCpuCores is summed from each host's hardware CpuInfo.
#>
[CmdletBinding()]
param(
)
Connect-ITDvCenter -Credential $Secret:ndgov_svcitdvmvcro
#region --- Setup ---------------------------------------------------------------
[string] $OutputPath = 'C:\temp\VM_Trends\'
[string] $ServerInstance = 'itdintsql22p1.nd.gov\INTSQL22P1'
[string] $Database = 'ITD-Systems-Automation'
[string] $Table = 'VMware_Trends_Cluster'
[System.Management.Automation.PSCredential] $SqlCredential = $Secret:sql_itdpsu1
$RunDate = Get-Date
$DateStamp = $RunDate.ToString('yyyyMMdd')
$Timestamp = $RunDate.ToString('yyyy-MM-dd')
if (-not (Test-Path -Path $OutputPath)) {
New-Item -ItemType Directory -Path $OutputPath | Out-Null
}
Start-Transcript -Path (Join-Path $OutputPath "ClusterMetadataReport_$DateStamp.log") -Append
#endregion
#region --- Build Cluster -> Datacenter Lookup ---------------------------------
Write-Verbose 'Building cluster-to-datacenter map...'
$ClusterDatacenterMap = @{}
Get-Datacenter | ForEach-Object {
$DatacenterName = $_.Name
Get-Cluster -Location $_ | ForEach-Object {
$ClusterDatacenterMap[$_.Name] = $DatacenterName
}
}
#endregion
#region --- Collect Cluster Data -----------------------------------------------
Write-Verbose 'Gathering clusters...'
$AllClusters = Get-Cluster
Write-Verbose "Processing $($AllClusters.Count) clusters..."
$Results = foreach ($Cluster in $AllClusters) {
$Summary = $Cluster.ExtensionData.Summary # reuse for all summary fields
#--- Aggregate host-level stats that vCenter summary does not expose
$ClusterHosts = Get-VMHost -Location $Cluster
$TotalCpuCores = ($ClusterHosts | ForEach-Object {
$_.ExtensionData.Hardware.CpuInfo.NumCpuCores
} | Measure-Object -Sum).Sum
$UsedMemoryGB = [Math]::Round(
($ClusterHosts | Measure-Object -Property MemoryUsageGB -Sum).Sum, 2
)
$VMCount = ($ClusterHosts | ForEach-Object {
($_.ExtensionData.Vm | Measure-Object).Count
} | Measure-Object -Sum).Sum
#--- Capacity from cluster summary
$TotalCpuMhz = $Summary.TotalCpu # MHz
$EffectiveCpuMhz = $Summary.EffectiveCpu # MHz
$TotalMemoryGB = [Math]::Round($Summary.TotalMemory / 1GB, 2) # bytes -> GB
$EffectiveMemGB = [Math]::Round($Summary.EffectiveMemory / 1KB, 2) # MB -> GB
[PSCustomObject]@{
ReportDate = $Timestamp
ClusterName = $Cluster.Name
Datacenter = $ClusterDatacenterMap[$Cluster.Name]
DrsEnabled = $Cluster.DrsEnabled
DrsMode = $Cluster.DrsAutomationLevel
HaEnabled = $Cluster.HAEnabled
HostCount = $Summary.NumHosts
EffectiveHostCount = $Summary.NumEffectiveHosts
TotalCpuCores = [int]$TotalCpuCores
TotalCpuMhz = $TotalCpuMhz
EffectiveCpuMhz = $EffectiveCpuMhz
TotalMemoryGB = $TotalMemoryGB
EffectiveMemoryGB = $EffectiveMemGB
UsedMemoryGB = $UsedMemoryGB
VMCount = [int]$VMCount
}
}
#endregion
#region --- Export CSV ---------------------------------------------------------
$OutputFile = Join-Path $OutputPath "ClusterMetadata_$DateStamp.csv"
$Results | Export-Csv -Path $OutputFile -NoTypeInformation
Write-Verbose "Exported $($Results.Count) cluster records to: $OutputFile"
#endregion
#region --- SQL Insert ---------------------------------------------------------
$DataTable = [System.Data.DataTable]::new()
$ColDefs = [ordered]@{
ReportDate = [datetime]
ClusterName = [string]
Datacenter = [string]
DrsEnabled = [bool]
DrsMode = [string]
HaEnabled = [bool]
HostCount = [int]
EffectiveHostCount = [int]
TotalCpuCores = [int]
TotalCpuMhz = [int]
EffectiveCpuMhz = [int]
TotalMemoryGB = [decimal]
EffectiveMemoryGB = [decimal]
UsedMemoryGB = [decimal]
VMCount = [int]
}
foreach ($Col in $ColDefs.GetEnumerator()) {
$Column = [System.Data.DataColumn]::new($Col.Key, $Col.Value)
$Column.AllowDBNull = $true
[void]$DataTable.Columns.Add($Column)
}
foreach ($Row in $Results) {
$DataRow = $DataTable.NewRow()
foreach ($Col in $ColDefs.Keys) {
$Val = $Row.$Col
$DataRow[$Col] = if ($null -ne $Val) { $Val } else { [DBNull]::Value }
}
[void]$DataTable.Rows.Add($DataRow)
}
$SqlParams = @{
ServerInstance = $ServerInstance
DatabaseName = $Database
SchemaName = 'dbo'
TableName = $Table
Credential = $SqlCredential
InputData = $DataTable
}
Write-SqlTableData @SqlParams
Write-Verbose "Inserted $($DataTable.Rows.Count) cluster records into [$Database].[dbo].[$Table]"
#endregion
Disconnect-ITDvCenter
#region --- Cleanup -------------------------------------------------------------
Stop-Transcript
#endregion
@@ -0,0 +1,216 @@
<#
.SYNOPSIS
Daily VMware Host metadata report for PowerBI trending and hardware capacity planning.
.DESCRIPTION
Collects ESXi host metadata from vCenter including hardware, compute capacity, and
version information. Exports a timestamped CSV and inserts into SQL each run.
Append daily runs to build a historical dataset for PowerBI trend analysis and
physical hardware purchasing / lifecycle decisions.
.NOTES
Run the following DDL once to create the destination table:
DROP TABLE IF EXISTS [dbo].[VMware_Trends_Host]
CREATE TABLE [dbo].[VMware_Trends_Host] (
[ReportDate] DATETIME2 NOT NULL,
[HostName] NVARCHAR(255) NOT NULL,
[Datacenter] NVARCHAR(100) NULL,
[Cluster] NVARCHAR(100) NULL,
[ConnectionState] NVARCHAR(50) NULL,
[PowerState] NVARCHAR(50) NULL,
[InMaintenanceMode] BIT NOT NULL,
[Model] NVARCHAR(255) NULL,
[ProcessorType] NVARCHAR(255) NULL,
[CpuSockets] INT NULL,
[CoresPerSocket] INT NULL,
[TotalCpuCores] INT NULL,
[CpuMhz] INT NULL,
[TotalCpuMhz] INT NULL,
[MemoryTotalGB] DECIMAL(10,2) NULL,
[MemoryUsageGB] DECIMAL(10,2) NULL,
[VMCount] INT NULL,
[EsxiVersion] NVARCHAR(50) NULL,
[EsxiBuild] NVARCHAR(50) NULL,
[UptimeDays] DECIMAL(10,2) NULL
)
MemoryUsageGB reflects real-time memory consumed by running VMs at the time of collection.
UptimeDays will be NULL for hosts that are powered off or disconnected.
#>
[CmdletBinding()]
param(
)
Connect-ITDvCenter -Credential $Secret:ndgov_svcitdvmvcro
#region --- Setup ---------------------------------------------------------------
[string] $OutputPath = 'C:\temp\VM_Trends\'
[string] $ServerInstance = 'itdintsql22p1.nd.gov\INTSQL22P1'
[string] $Database = 'ITD-Systems-Automation'
[string] $Table = 'VMware_Trends_Host'
[System.Management.Automation.PSCredential] $SqlCredential = $Secret:sql_itdpsu1
$RunDate = Get-Date
$DateStamp = $RunDate.ToString('yyyyMMdd')
$Timestamp = $RunDate.ToString('yyyy-MM-dd')
if (-not (Test-Path -Path $OutputPath)) {
New-Item -ItemType Directory -Path $OutputPath | Out-Null
}
Start-Transcript -Path (Join-Path $OutputPath "VMHostMetadataReport_$DateStamp.log") -Append
#endregion
#region --- Build VMHost -> Cluster/Datacenter Lookups (avoids per-host queries) -
Write-Verbose 'Building host-to-cluster and host-to-datacenter maps...'
$HostClusterMap = @{}
$HostDatacenterMap = @{}
Get-Datacenter | ForEach-Object {
$DatacenterName = $_.Name
Get-VMHost -Location $_ | ForEach-Object {
$HostDatacenterMap[$_.Name] = $DatacenterName
}
}
Get-Cluster | ForEach-Object {
$ClusterName = $_.Name
Get-VMHost -Location $_ | ForEach-Object {
$HostClusterMap[$_.Name] = $ClusterName
}
}
#endregion
#region --- Collect Host Data --------------------------------------------------
Write-Verbose 'Gathering hosts...'
$AllHosts = $HostDatacenterMap.Keys | ForEach-Object { Get-VMHost -Name $_ }
Write-Verbose "Processing $($AllHosts.Count) hosts..."
$Results = foreach ($VMHost in $AllHosts) {
$Ext = $VMHost.ExtensionData # single API object -- reuse for all fields
#--- CPU info from hardware summary
$CpuSockets = $Ext.Hardware.CpuInfo.NumCpuPackages
$TotalCpuCores = $Ext.Hardware.CpuInfo.NumCpuCores
$CoresPerSocket = if ($CpuSockets -gt 0) { [int]($TotalCpuCores / $CpuSockets) } else { $null }
$CpuMhz = $Ext.Hardware.CpuInfo.Hz / 1000000 # Hz -> MHz
$TotalCpuMhz = $Ext.Summary.Hardware.CpuMhz * $TotalCpuCores
#--- Memory
$MemoryTotalGB = [Math]::Round($VMHost.MemoryTotalGB, 2)
$MemoryUsageGB = [Math]::Round($VMHost.MemoryUsageGB, 2)
#--- VM count on this host right now
$VMCount = ($Ext.Vm | Measure-Object).Count
#--- Uptime (null when host is powered off / disconnected)
$UptimeDays = $null
$BootTime = $Ext.Runtime.BootTime
if ($null -ne $BootTime) {
$UptimeDays = [Math]::Round(($RunDate - $BootTime).TotalDays, 2)
}
[PSCustomObject]@{
ReportDate = $Timestamp
HostName = $VMHost.Name
Datacenter = $HostDatacenterMap[$VMHost.Name]
Cluster = $HostClusterMap[$VMHost.Name]
ConnectionState = $VMHost.ConnectionState
PowerState = $VMHost.PowerState
InMaintenanceMode = $VMHost.ExtensionData.Runtime.InMaintenanceMode
Model = $Ext.Hardware.SystemInfo.Model
ProcessorType = $VMHost.ProcessorType
CpuSockets = $CpuSockets
CoresPerSocket = $CoresPerSocket
TotalCpuCores = $TotalCpuCores
CpuMhz = [int]$CpuMhz
TotalCpuMhz = $TotalCpuMhz
MemoryTotalGB = $MemoryTotalGB
MemoryUsageGB = $MemoryUsageGB
VMCount = $VMCount
EsxiVersion = $VMHost.Version
EsxiBuild = $VMHost.Build
UptimeDays = $UptimeDays
}
}
#endregion
#region --- Export CSV ---------------------------------------------------------
$OutputFile = Join-Path $OutputPath "VMHostMetadata_$DateStamp.csv"
$Results | Export-Csv -Path $OutputFile -NoTypeInformation
Write-Verbose "Exported $($Results.Count) host records to: $OutputFile"
#endregion
#region --- SQL Insert ---------------------------------------------------------
$DataTable = [System.Data.DataTable]::new()
$ColDefs = [ordered]@{
ReportDate = [datetime]
HostName = [string]
Datacenter = [string]
Cluster = [string]
ConnectionState = [string]
PowerState = [string]
InMaintenanceMode = [bool]
Model = [string]
ProcessorType = [string]
CpuSockets = [int]
CoresPerSocket = [int]
TotalCpuCores = [int]
CpuMhz = [int]
TotalCpuMhz = [int]
MemoryTotalGB = [decimal]
MemoryUsageGB = [decimal]
VMCount = [int]
EsxiVersion = [string]
EsxiBuild = [string]
UptimeDays = [decimal]
}
foreach ($Col in $ColDefs.GetEnumerator()) {
$Column = [System.Data.DataColumn]::new($Col.Key, $Col.Value)
$Column.AllowDBNull = $true
[void]$DataTable.Columns.Add($Column)
}
foreach ($Row in $Results) {
$DataRow = $DataTable.NewRow()
foreach ($Col in $ColDefs.Keys) {
$Val = $Row.$Col
$DataRow[$Col] = if ($null -ne $Val) { $Val } else { [DBNull]::Value }
}
[void]$DataTable.Rows.Add($DataRow)
}
$SqlParams = @{
ServerInstance = $ServerInstance
DatabaseName = $Database
SchemaName = 'dbo'
TableName = $Table
Credential = $SqlCredential
InputData = $DataTable
}
Write-SqlTableData @SqlParams
Write-Verbose "Inserted $($DataTable.Rows.Count) host records into [$Database].[dbo].[$Table]"
Disconnect-ITDvCenter
#endregion
#region --- Cleanup -------------------------------------------------------------
Stop-Transcript
#endregion
@@ -0,0 +1,300 @@
<#
.SYNOPSIS
Daily VM metadata report for PowerBI trending and hardware capacity planning.
.DESCRIPTION
Collects VM metadata from vCenter including compute, storage, OS, and VMware Tools
information using only data available within vCenter (no direct guest connections).
Exports a timestamped CSV each run -- append daily runs to build a historical dataset
suitable for PowerBI trend analysis and physical hardware purchasing decisions.
.PARAMETER vCenterServers
One or more vCenter server hostnames. Defaults to itdvmvc1.nd.gov and itdvmvc2.nd.gov.
.PARAMETER DatacenterFilter
Wildcard filter applied to datacenter names. Defaults to 'Primary*'.
.PARAMETER OutputPath
Directory where CSV and log files are written.
Defaults to C:\ITDSCRIPT\Reports\VMMetadata.
.PARAMETER CredentialPath
Path to a saved PSCredential XML file for unattended/scheduled runs.
Create one interactively with:
Get-Credential | Export-Clixml -Path C:\ITDSCRIPT\Creds\vCenter.xml
When omitted the script prompts for credentials.
.EXAMPLE
# Interactive run
.\VMware-VMDailyMetadataReport.ps1
.EXAMPLE
# Scheduled / unattended run
.\VMware-VMDailyMetadataReport.ps1 -CredentialPath 'C:\ITDSCRIPT\Creds\vCenter.xml'
.NOTES
Run the following DDL once to create the destination table:
DROP TABLE IF EXISTS [dbo].[VMware_Trends_VM]
CREATE TABLE [dbo].[VMware_Trends_VM] (
[ReportDate] DATETIME2 NOT NULL,
[VMName] NVARCHAR(255) NOT NULL,
[Datacenter] NVARCHAR(100) NULL,
[Cluster] NVARCHAR(100) NULL,
[PowerState] NVARCHAR(50) NULL,
[IsSRMPlaceholder] BIT NOT NULL,
[StoragePlatform] NVARCHAR(100) NULL,
[GuestOS] NVARCHAR(255) NULL,
[vCPUs] INT NULL,
[MemoryGB] DECIMAL(10,2) NULL,
[ProvisionedSpaceGB] DECIMAL(12,2) NULL,
[UsedSpaceGB] DECIMAL(12,2) NULL,
[GuestDiskCapacityGB] DECIMAL(12,2) NULL,
[GuestDiskUsedGB] DECIMAL(12,2) NULL,
[ToolsRunningStatus] NVARCHAR(100) NULL,
[ToolsVersionStatus] NVARCHAR(100) NULL,
[ToolsVersion] NVARCHAR(50) NULL,
[Tag_DRProtection] NVARCHAR(100) NULL,
[Tag_AppName] NVARCHAR(255) NULL,
[Tag_VRDatastores] NVARCHAR(255) NULL,
[Tag_VRRPO] NVARCHAR(100) NULL,
[Tag_DTAP] NVARCHAR(50) NULL,
[Tag_StartupPriority] NVARCHAR(100) NULL,
[Tag_SRMRecoveryType] NVARCHAR(100) NULL,
[Tag_LicensingRestrictions] NVARCHAR(255) NULL
)
Guest OS disk capacity / used columns are NULL for powered-off VMs and SRM placeholders.
#>
[CmdletBinding()]
param(
)
Connect-ITDvCenter -Credential $Secret:ndgov_svcitdvmvcro
#region --- Setup ---------------------------------------------------------------
[string] $OutputPath = 'C:\temp\VM_Trends\'
[string] $ServerInstance = 'itdintsql22p1.nd.gov\INTSQL22P1'
[string] $Database = 'ITD-Systems-Automation'
[string] $Table = 'VMware_Trends_VM'
[System.Management.Automation.PSCredential] $SqlCredential = $Secret:sql_itdpsu1
$RunDate = Get-Date
$DateStamp = $RunDate.ToString('yyyyMMdd')
$Timestamp = $RunDate.ToString('yyyy-MM-dd')
if (-not (Test-Path -Path $OutputPath)) {
New-Item -ItemType Directory -Path $OutputPath | Out-Null
}
Start-Transcript -Path (Join-Path $OutputPath "VMMetadataReport_$DateStamp.log") -Append
#endregion
#region --- Build VMHost -> Cluster/Datacenter Lookups (avoids per-VM API calls) -
Write-Verbose 'Building host-to-cluster and host-to-datacenter maps...'
$HostClusterMap = @{}
$HostDatacenterMap = @{}
Get-Datacenter | ForEach-Object {
$DatacenterName = $_.Name
Get-VMHost -Location $_ | ForEach-Object {
$HostDatacenterMap[$_.Name] = $DatacenterName
}
}
Get-Cluster | ForEach-Object {
$ClusterName = $_.Name
Get-VMHost -Location $_ | ForEach-Object {
$HostClusterMap[$_.Name] = $ClusterName
}
}
#endregion
#region --- Collect VM Data -----------------------------------------------------
Write-Verbose "Gathering VMs"
# Include ALL VMs (SRM placeholders flagged via column, not excluded).
# vCLS agent VMs are excluded -- they are vSphere internal and not customer workloads.
$AllVMs = Get-VM | Where-Object { $_.Name -notlike 'vCLS*' }
#--- Pre-fetch all tag assignments in one API call (avoids per-VM Get-TagAssignment)
Write-Verbose 'Pre-fetching VM tag assignments...'
$TagLookup = @{}
Get-TagAssignment -Entity $AllVMs | ForEach-Object {
$VMId = $_.Entity.Id
$Cat = $_.Tag.Category.Name
$TagName = $_.Tag.Name
if (-not $TagLookup.ContainsKey($VMId)) { $TagLookup[$VMId] = @{} }
if ($TagLookup[$VMId].ContainsKey($Cat)) {
$TagLookup[$VMId][$Cat] += "; $TagName"
} else {
$TagLookup[$VMId][$Cat] = $TagName
}
}
Write-Verbose "Processing $($AllVMs.Count) VMs..."
$Results = foreach ($VM in $AllVMs) {
$Ext = $VM.ExtensionData # single API object -- reuse for all fields
#--- SRM placeholder detection
$IsSRMPlaceholder = $Ext.Summary.Config.ManagedBy.Type -eq 'placeholderVm'
#--- Cluster / Datacenter (null-safe: standalone hosts have no cluster entry)
$ClusterName = $HostClusterMap[$VM.VMHost.Name]
$DatacenterName = $HostDatacenterMap[$VM.VMHost.Name]
#--- Tag assignments (pre-fetched; null when category not assigned to this VM)
$VMTags = if ($TagLookup.ContainsKey($VM.Id)) { $TagLookup[$VM.Id] } else { @{} }
#--- Storage platform parsed from datastore name convention: VMCLUSTER_LUN_PLATFORM_Desc
# Segment 2 = storage platform identifier (e.g. FS92, A9K).
# Cluster grouping uses the compute Cluster column -- no need to re-derive it here.
$StoragePlatforms = foreach ($DSName in $Ext.Config.DatastoreUrl.Name) {
$Segments = $DSName -split '_'
if ($Segments.Count -ge 3) { $Segments[2] }
}
$StoragePlatform = ($StoragePlatforms | Sort-Object -Unique) -join '; '
#--- VMware Tools guest disk info
# Populated only when Tools is running; null otherwise.
$GuestDiskCapacityGB = $null
$GuestDiskUsedGB = $null
if ($Ext.Guest.Disk) {
$TotalCapBytes = ($Ext.Guest.Disk | Measure-Object -Property Capacity -Sum).Sum
$TotalFreeBytes = ($Ext.Guest.Disk | Measure-Object -Property FreeSpace -Sum).Sum
$GuestDiskCapacityGB = [Math]::Round($TotalCapBytes / 1GB, 2)
$GuestDiskUsedGB = [Math]::Round(($TotalCapBytes - $TotalFreeBytes) / 1GB, 2)
}
[PSCustomObject]@{
# --- Identity & grouping
ReportDate = $Timestamp # for PowerBI time-series/trend axis
VMName = $VM.Name
Datacenter = $DatacenterName
Cluster = $ClusterName
PowerState = $VM.PowerState
IsSRMPlaceholder = $IsSRMPlaceholder
StoragePlatform = $StoragePlatform
GuestOS = $Ext.Guest.GuestFullName
# --- Compute
vCPUs = $VM.NumCpu
MemoryGB = $VM.MemoryGB
# --- Datastore-level storage
# ProvisionedSpaceGB : maximum the VM could consume (thin disks counted at max size)
# UsedSpaceGB : bytes actually committed on datastores right now
ProvisionedSpaceGB = [Math]::Round($VM.ProvisionedSpaceGB, 2)
UsedSpaceGB = [Math]::Round($VM.UsedSpaceGB, 2)
# --- Guest OS-level storage (from VMware Tools; null when Tools not running)
# GuestDiskCapacityGB : sum of all volume capacities seen inside the guest
# GuestDiskUsedGB : sum of space consumed across those volumes
GuestDiskCapacityGB = $GuestDiskCapacityGB
GuestDiskUsedGB = $GuestDiskUsedGB
# --- VMware Tools
# ToolsRunningStatus : guestToolsRunning | guestToolsNotRunning | guestToolsExecutingScripts
# ToolsVersionStatus : guestToolsCurrent | guestToolsNeedUpgrade | guestToolsUnmanaged | guestToolsTooNew
# ToolsVersion : numeric build version string reported by vCenter
ToolsRunningStatus = $Ext.Guest.ToolsRunningStatus
ToolsVersionStatus = $Ext.Guest.ToolsVersionStatus
ToolsVersion = $Ext.Guest.ToolsVersion
# --- vCenter Tags
Tag_DRProtection = $VMTags['DR Protection']
Tag_AppName = $VMTags['AppName']
Tag_VRDatastores = $VMTags['VR Datastores']
Tag_VRRPO = $VMTags['VR RPO']
Tag_DTAP = $VMTags['DTAP']
Tag_StartupPriority = $VMTags['StartupPriority']
Tag_SRMRecoveryType = $VMTags['SRM Recovery Type']
Tag_LicensingRestrictions = $VMTags['LicensingRestrictions']
}
}
#endregion
<#region --- Export CSV ---------------------------------------------------------
$OutputFile = Join-Path $OutputPath "VMMetadata_$DateStamp.csv"
$Results | Export-Csv -Path $OutputFile -NoTypeInformation
Write-Verbose "Exported $($Results.Count) VM records to: $OutputFile"
#endregion
#>
#region --- SQL Insert ---------------------------------------------------------
# Build a typed DataTable so Write-SqlTableData knows each column's type
# even when nullable columns contain null values. Type inference from raw
# PSCustomObjects fails when the first row has a null in a numeric column.
$DataTable = [System.Data.DataTable]::new()
$ColDefs = [ordered]@{
ReportDate = [datetime]
VMName = [string]
Datacenter = [string]
Cluster = [string]
PowerState = [string]
IsSRMPlaceholder = [bool]
StoragePlatform = [string]
GuestOS = [string]
vCPUs = [int]
MemoryGB = [decimal]
ProvisionedSpaceGB = [decimal]
UsedSpaceGB = [decimal]
GuestDiskCapacityGB = [decimal]
GuestDiskUsedGB = [decimal]
ToolsRunningStatus = [string]
ToolsVersionStatus = [string]
ToolsVersion = [string]
Tag_DRProtection = [string]
Tag_AppName = [string]
Tag_VRDatastores = [string]
Tag_VRRPO = [string]
Tag_DTAP = [string]
Tag_StartupPriority = [string]
Tag_SRMRecoveryType = [string]
Tag_LicensingRestrictions = [string]
}
foreach ($Col in $ColDefs.GetEnumerator()) {
$Column = [System.Data.DataColumn]::new($Col.Key, $Col.Value)
$Column.AllowDBNull = $true
[void]$DataTable.Columns.Add($Column)
}
foreach ($Row in $Results) {
$DataRow = $DataTable.NewRow()
foreach ($Col in $ColDefs.Keys) {
$Val = $Row.$Col
$DataRow[$Col] = if ($null -ne $Val) { $Val } else { [DBNull]::Value }
}
[void]$DataTable.Rows.Add($DataRow)
}
$SqlParams = @{
ServerInstance = $ServerInstance
DatabaseName = $Database
SchemaName = 'dbo'
TableName = $Table
Credential = $SqlCredential
InputData = $DataTable
}
Write-SqlTableData @SqlParams
Write-Verbose "Inserted $($DataTable.Rows.Count) VM records into [$Database].[dbo].[$Table]"
#endregion
#region --- Cleanup -------------------------------------------------------------
Stop-Transcript
#endregion
@@ -0,0 +1,247 @@
<# NOT FUNCTIONAL YET - WORK IN PROGRESS
.SYNOPSIS
Update/renew any expired iLO certificates in the VMware/Synergy environment
.DESCRIPTION
Update/renew any expired iLO certificates in the VMware/Synergy environment
.NOTES
# retrieve all iLO from Synergy
# find all certificates expiring in the next 3 days, or ones that do not have nd.gov in name, add to an array
# loop through list
## connect to ilo
## generate CSR
## send CSR to sectigo to generate new
## wait for approval
## download new cert
## upload/update/set on iLO
.LINK
.EXAMPLE
Test-MyTestFunction -Verbose
Explanation of the function or its result. You can include multiple examples with additional .EXAMPLE lines
#>
[CmdletBinding()]
param (
[Parameter(ParameterSetName = 'ByHostName')]
[string[]]
$HostName = $null,
[Parameter(ParameterSetName = 'SynergyDiscovery')]
[switch]
$SynergyDiscovery
)
function Get-SectigoToken {
Write-Verbose -Message "Retrieving Sectigo API key for VMware" -Verbose
$SectigoCred = Get-ITDPassword -Title "Sectigo API key for VMware" -UserName "f595aa76-c26b-4664-b95d-cb805cc7ff4e"
Write-Verbose -Message "Confirming SectigoCred is $($SectigoCred.UserName)" -Verbose
$AuthBody = @{
grant_type = 'client_credentials'
#client_id = $Secret:sectigo_vmware.UserName
#client_secret = $Secret:sectigo_vmware.GetNetworkCredential().Password
client_id = $SectigoCred.UserName
client_secret = $SectigoCred.GetNetworkCredential().Password
}
$AuthBaseAPIUrl = 'https://auth.sso.sectigo.com'
$tokenEndpoint = $AuthBaseAPIUrl + '/auth/realms/apiclients/protocol/openid-connect/token'
$env:SectigoToken = (Invoke-RestMethod -Method Post -Uri $tokenEndpoint -ContentType 'application/x-www-form-urlencoded' -Body $AuthBody).access_token
Return $env:SectigoToken
}
Write-Verbose -Message "Retrieving OneView Service Account credentials" -Verbose
$OVCred = Get-ITDPassword -Title "VMware iLO Service Account" -UserName "ndgov\svcitdvmhpe"
Write-Verbose -Message "Confirming OneView is $($OVCred.UserName)" -Verbose
Import-Module HPEiLOCmdlets -Force
Write-Verbose -Message "Gather PSUniversal Job Information" -Verbose
$PSUJobId = $UAJob.Id
Write-Verbose -Message "Set static variables" -Verbose
$RequesterEmail = 'vmware@nd.gov'
$AppName = "Infra-VMware"
$ServerTypeCode = "Linux"
$Format = "x509CO"
$OrgId = 8133 # Sectigo OrgDept ID for State of North Dakota Information Technology Department - Cloud & Infrastructure
$CertType = 2375 # Sectigo Cert Type for Standard SSL Multi-Domain
$BaseAPIUrl = 'https://admin.hard.sectigo.com'
switch ($PSCmdlet.ParameterSetName) {
'ByHostName' {
$AllILOServers = $HostName
}
'SynergyDiscovery' {
Write-Verbose -Message "Retrieving iLO information from OneView/Synergy" -Verbose
$OneviewServers = @('itdmdnsyncompt1.nd.gov', 'itdmdnsyncompp1.nd.gov', 'itdbissyncompp1.nd.gov')
$AllILOServers = @()
ForEach ($OneviewServer in $OneViewServers ) {
Write-Verbose -Message "Connecting to OneView server $OneviewServer" -Verbose
Connect-OVMgmt -Hostname $OneviewServer -Credential $OVCred -AuthLoginDomain nd.gov -LoginAcknowledge
$AllILOServers += (Get-OVServer).ServerName | ForEach-Object {
$_.split('.')[0] + "lo.nd.gov"
}
Disconnect-OVMgmt
}
}
default {
Write-Error -Message "Invalid parameter set."
exit 1
}
}
Write-Verbose -Message "Checking iLO certificates for expiration or invalid issuer/commonname" -Verbose
$AlliLOToRenew = @()
ForEach ($iLODnsName in $AllILOServers) {
Write-Verbose -Message "Checking certificate for iLO $iLODnsName" -Verbose
$cert = Get-SslCertificate -DNSName $iLODnsName
If ( $cert.NotAfter.AddDays(-7) -le (Get-Date) ) {
Write-Warning -Message "Certificate for $iLODnsName expires on $($cert.NotAfter), add to renewal list."
$AlliLOToRenew += $iLODnsName
}
If ( $cert.subject -notlike "*.nd.gov*") {
Write-Warning -Message "Certificate for $iLODnsName is not a ND.gov cert, add to renewal list."
$AlliLOToRenew += $iLODnsName
}
}
ForEach ($iLOToRenew in $AlliLOToRenew | Select-Object -Unique) {
Write-Verbose -Message "Processing iLO $iLOToRenew for certificate renewal" -Verbose
$iLOCred = $null
switch ($iLOToRenew) {
{ $_ -like "*bis*" } {
Write-Verbose -Message "BIS iLO detected, getting credentials for itdbissyncompp1" -Verbose
$iloCred = Get-ITDPassword -Title "itdbissyncompp1 iLO" -UserName "Administrator";
}
{ $_ -like "*mdn*" } {
Write-Verbose -Message "MDN iLO detected, getting credentials for itdmdnsyncompp1" -Verbose
$iloCred = Get-ITDPassword -Title "itdmdnsyncompp1 iLO" -UserName "Administrator";
}
{ $_ -like "*test*" } {
Write-Verbose -Message "TEST iLO detected, getting credentials for itdmdnsyncompt1" -Verbose
$iloCred = Get-ITDPassword -Title "itdmdnsyncompt1 iLO" -UserName "Administrator";
}
default { Write-Error -Message "No iLO credentials found for $iLOToRenew, skipping."; continue; }
}
try {
Write-Verbose -Message "Establishing connection to iLO $iLOToRenew" -Verbose
$iLOConnection = Connect-HPEiLO -Address $iLOToRenew -Credential $iLOCred -DisableCertificateAuthentication
Write-Verbose -Message "Generating CSR on iLO $iLOToRenew" -Verbose
Start-HPEiLOCertificateSigningRequest -Connection $iLOConnection `
-CommonName $iLOConnection.Hostname `
-Organization "State of North Dakota" `
-Country US `
-City Bismarck `
-State "North Dakota"
Start-Sleep -Seconds 30 ### for some reason if you check iLO for CSR too frequently it doesn't work
Write-Verbose -Message "Getting CSR for $iLOToRenew"
$CsrData = $null
While ($null -eq $CsrData.CertificateSigningRequest) {
try {
$CsrData = Get-HPEiLOCertificateSigningRequest -Connection $iLOConnection
}
catch {
Write-Warning -Message "CSR not ready yet for $iLOToRenew, waiting 10 seconds."
Start-Sleep -Seconds 10
}
}
Disconnect-HPEiLO -Connection $iLOConnection
$iLOConnection = $null
Write-Verbose -Message "Submitting CSR to Sectigo for $iLOToRenew" -Verbose
#Get-SectigoToken ## function above loaded into memory
$EnrollBody = @{
orgId = $OrgId;
certType = $CertType
term = 365;
comments = "iLO Certificate Renewal for $iLOToRenew"
serverType = $ServerTypeCode
csr = $CsrData.CertificateSigningRequest
externalRequester = "vmware@nd.gov"
customFields = @(
@{
name = 'ApplicationName'
value = 'Infra-VMware'
}
)
}
$EnrollParams = @{
Method = 'Post'
Uri = $BaseAPIUrl + "/api/ssl/v1/enroll"
Headers = @{
"Authorization" = ("Bearer " + (Get-SectigoToken))
"Content-Type" = "application/json"
}
Body = ($EnrollBody | ConvertTo-Json -Depth 10)
ContentType = 'application/json'
}
$EnrollResponse = Invoke-RestMethod @EnrollParams
$OrderId = $EnrollResponse.sslId
Write-Verbose -Message "Waiting for certificate issuance for $iLOToRenew" -Verbose
$Certificate = $null
Start-Sleep -Seconds 15
While ($Certificate.status -ne "Issued") {
$ValidateUrl = "${BaseAPIUrl}/api/ssl/v1/${OrderId}"
$ValidateSplat = @{
Uri = $ValidateUrl
Method = 'Get'
Headers = @{
"Authorization" = ("Bearer " + (Get-SectigoToken))
"Content-Type" = "application/json"
}
}
$Certificate = Invoke-RestMethod @ValidateSplat
If ($Certificate.status -ne "Issued") {
Write-Warning -Message "Certificate for $iLOToRenew not issued yet, waiting 15 seconds."
Start-Sleep -Seconds 15
}
}
Write-Verbose -Message "Downloading issued certificate for $iLOToRenew" -Verbose
$CollectUrl = $BaseAPIUrl + "/api/ssl/v1/collect/${OrderId}?format=${Format}"
$CommonName = $Certificate.commonName
$DownloadSplat = @{
Uri = $CollectUrl
Method = 'Get'
Headers = @{
"Authorization" = ("Bearer " + (Get-SectigoToken))
"Content-Type" = "application/json"
}
UseBasicParsing = $true
}
Write-Verbose -Message "Downloading certificate to F:\iLO_certs\$CommonName-$OrderId-$PSUJobId.pem" -Verbose
Invoke-WebRequest @DownloadSplat -OutFile "F:\iLO_certs\$CommonName-$OrderId-$PSUJobId.pem"
Write-Verbose -Message "Importing new certificate to iLO $iLOToRenew" -Verbose
$CertificateToUpload = Get-Content -Path "F:\iLO_certs\$CommonName-$OrderId-$PSUJobId.pem" -Raw
$iLOConnection = Connect-HPEiLO -Address $iLOToRenew -Credential $iLOCred -DisableCertificateAuthentication -Verbose
Import-HPEiLOCertificate -Certificate ($CertificateToUpload | Out-String) -Connection $iLOConnection -Force
Disconnect-HPEiLO -Connection $iLOConnection
Write-Verbose -Message "Disconnecting from iLO $iLOToRenew" -Verbose
}
catch {
}
}