From de609fe8ee2ccc71478c0302c08627107f3305cc Mon Sep 17 00:00:00 2001 From: Brian O'Connell Date: Thu, 28 Aug 2025 15:29:02 +0100 Subject: [PATCH] refactor: updates for vcf9 Signed-off-by: Brian O'Connell --- VMware.CloudFoundation.InstanceRecovery.psm1 | 1182 +++++++++++++----- 1 file changed, 866 insertions(+), 316 deletions(-) diff --git a/VMware.CloudFoundation.InstanceRecovery.psm1 b/VMware.CloudFoundation.InstanceRecovery.psm1 index a41bc9f..56fd839 100644 --- a/VMware.CloudFoundation.InstanceRecovery.psm1 +++ b/VMware.CloudFoundation.InstanceRecovery.psm1 @@ -12,7 +12,8 @@ If ($PSEdition -eq 'Core') { "invoke-restmethod:SkipCertificateCheck" = $true "invoke-webrequest:SkipCertificateCheck" = $true } -} else { +} +else { Add-Type @" using System.Net; using System.Security.Cryptography.X509Certificates; @@ -86,7 +87,7 @@ Function LogMessage { Param ( [Parameter (Mandatory = $true)] [AllowEmptyString()] [String]$message, [Parameter (Mandatory = $false)] [Switch]$nonewline, - [Parameter (Mandatory = $false)] [ValidateSet("INFO", "ERROR", "WARNING", "EXCEPTION","ADVISORY","NOTE","QUESTION","WAIT")] [String]$type = "INFO" + [Parameter (Mandatory = $false)] [ValidateSet("INFO", "ERROR", "WARNING", "EXCEPTION", "ADVISORY", "NOTE", "QUESTION", "WAIT")] [String]$type = "INFO" ) If (!$colour) { @@ -95,11 +96,14 @@ Function LogMessage { If ($type -eq "INFO") { $messageColour = "92m" #Green - } elseIf ($type -in "ERROR","EXCEPTION") { + } + elseIf ($type -in "ERROR", "EXCEPTION") { $messageColour = "91m" # Red - } elseIf ($type -in "WARNING","ADVISORY","QUESTION") { + } + elseIf ($type -in "WARNING", "ADVISORY", "QUESTION") { $messageColour = "93m" #Yellow - } elseIf ($type -in "NOTE","WAIT") { + } + elseIf ($type -in "NOTE", "WAIT") { $messageColour = "97m" # White } @@ -123,7 +127,8 @@ Function LogMessage { If ($nonewline) { Write-Host "$ESC[${timestampcolour} [$timestamp]$ESC[${messageColour} [$type] $message$ESC[0m" -NoNewline - } else { + } + else { Write-Host "$ESC[${timestampcolour} [$timestamp]$ESC[${messageColour} [$type] $message$ESC[0m" } #$logContent = '[' + $timeStamp + '] [' +$threadTag + '] ' + $type + ' ' + $message @@ -248,33 +253,38 @@ Function Confirm-VCFInstanceRecoveryPreReqs { $is7Zip4PowerShellInstalled = Get-InstalledModule -name "7Zip4PowerShell" -RequiredVersion "2.4.0" -ErrorAction SilentlyContinue If (!$is7Zip4PowerShellInstalled) { LogMessage -type WARNING -message "[$jumpboxName] 7Zip4PowerShell Module Missing. Please install" - } else { + } + else { LogMessage -type INFO -message "[$jumpboxName] 7Zip4PowerShell Module found" } $isPoshSSHInstalled = Get-InstalledModule -name "Posh-SSH" -RequiredVersion "3.0.8" -ErrorAction SilentlyContinue If (!$isPoshSSHInstalled) { LogMessage -type WARNING -message "[$jumpboxName] Posh-SSH Module Missing. Please install" - } else { + } + else { LogMessage -type INFO -message "[$jumpboxName] Posh-SSH Module found" } $isPowerCLIInstalled = Get-InstalledModule -name "VMware.PowerCLI" -ErrorAction SilentlyContinue If (!$isPowerCLIInstalled) { LogMessage -type WARNING -message "[$jumpboxName] PowerCLI Module Missing. Please install" - } else { + } + else { LogMessage -type INFO -message "[$jumpboxName] PowerCLI Module found" } $isPowerCLISddcmModuleInstalled = Get-InstalledModule -name "VMware.Sdk.Vcf.SddcManager" -RequiredVersion "5.1.0" -ErrorAction SilentlyContinue If (!$isPowerCLISddcmModuleInstalled) { LogMessage -type WARNING -message "[$jumpboxName] VMware.Sdk.Vcf.SddcManager Module Missing. Please install" - } else { + } + else { LogMessage -type INFO -message "[$jumpboxName] VMware.Sdk.Vcf.SddcManager Module found" } $isPowerCLICloudBuilderModuleInstalled = Get-InstalledModule -name "VMware.Sdk.Vcf.CloudBuilder" -RequiredVersion "5.1.0" -ErrorAction SilentlyContinue If (!$isPowerCLICloudBuilderModuleInstalled) { LogMessage -type WARNING -message "[$jumpboxName] VMware.Sdk.Vcf.CloudBuilder Module Missing. Please install" - } else { + } + else { LogMessage -type INFO -message "[$jumpboxName] VMware.Sdk.Vcf.CloudBuilder Module found" } @@ -286,10 +296,12 @@ Function Confirm-VCFInstanceRecoveryPreReqs { $Global:openSSLUrl = "https://slproweb.com" + $openSslLink If ($openSSLUrl) { LogMessage -type WARNING -message "[$jumpboxName] OpenSSL missing. Please install. Latest version detected is here: $openSSLUrl" - } else { + } + else { LogMessage -type WARNING -message "[$jumpboxName] OpenSSL missing. Please install. Unable to detect latest version on web" } - } else { + } + else { LogMessage -type INFO -message "[$jumpboxName] OpenSSL Utility found" } $pathEntries = $env:path -split (";") @@ -298,21 +310,21 @@ Function Confirm-VCFInstanceRecoveryPreReqs { $testOpenSSExe = Test-Path "$OpenSSLPath\openssl.exe" IF ($testOpenSSExe) { LogMessage -type INFO -message "[$jumpboxName] openssl.exe found in $OpenSSLPath" - } else { + } + else { LogMessage -type WARNING -message "[$jumpboxName] $OpenSSLPath was found in environment path, but no openssl.exe was found in that path" } - } else { + } + else { LogMessage -type WARNING -message "[$jumpboxName] No folder path that looks like OpenSSL was discovered in the environment path variable. Please double check that the location of OpenSSL is included in the path variable" } - $viServerModeConfig = (Get-PowerCLIConfiguration | Where-Object {$_.scope -eq "AllUsers"}).DefaultVIServerMode - If ($viServerModeConfig -eq 'Multiple') - { + $viServerModeConfig = (Get-PowerCLIConfiguration | Where-Object { $_.scope -eq "AllUsers" }).DefaultVIServerMode + If ($viServerModeConfig -eq 'Multiple') { LogMessage -type INFO -message "[$jumpboxName] DefaultVIServerMode is correctly set to 'Multiple'" } - else - { + else { LogMessage -type WARNING -message "[$jumpboxName] DefaultVIServerMode is not correctly set. Please run 'Set-PowerCLIConfiguration -DefaultVIServerMode Multiple' to correct" } } @@ -386,13 +398,13 @@ Function New-ExtractDataFromSDDCBackup { $metadataJSON = Get-Content "$parentFolder\$extractedBackupFolder\metadata.json" | ConvertFrom-JSON $dnsJSON = Get-Content "$parentFolder\$extractedBackupFolder\appliancemanager_dns_configuration.json" | ConvertFrom-JSON $ntpJSON = Get-Content "$parentFolder\$extractedBackupFolder\appliancemanager_ntp_configuration.json" | ConvertFrom-JSON - $mgmtVcenterMetadata = Get-Content -Path ($vCenterbackupFolderFullPath + "/backup-metadata.json") | ConvertFrom-JSON - $managementSubnetMask = cidrToMask $mgmtVcenterMetadata.PrimaryNetworkInfo.ipv4.prefix + #$mgmtVcenterMetadata = Get-Content -Path ($vCenterbackupFolderFullPath + "/backup-metadata.json") | ConvertFrom-JSON + #$managementSubnetMask = cidrToMask $mgmtVcenterMetadata.PrimaryNetworkInfo.ipv4.prefix $sddcManagerIP = $metadataJSON.ip - $managementSubnetMask = $metaDataJSON.netmask + #$managementSubnetMask = $metaDataJSON.netmask $ip = [ipaddress]$sddcManagerIP - $subnet = [ipaddress]$managementSubnetMask + $subnet = [ipaddress]$metaDataJSON.netmask $netid = [ipaddress]($ip.address -band $subnet.address) $managementSubnet = $($netid.ipaddresstostring) @@ -401,9 +413,9 @@ Function New-ExtractDataFromSDDCBackup { 'vsan_datastore' = $metadataJSON.vsan_datastore 'cluster' = $metaDataJSON.cluster 'datacenter' = $metaDataJSON.datacenter - 'netmask' = $managementSubnetMask + 'netmask' = $metaDataJSON.netmask 'subnet' = $managementSubnet - 'gateway' = $mgmtVcenterMetadata.PrimaryNetworkInfo.ipv4.defaultGateway + 'gateway' = $metaDataJSON.gateway 'domain' = $metaDataJSON.domain 'search_path' = $metaDataJSON.search_path 'primaryDnsServer' = $dnsJSON.primaryDnsServer @@ -427,7 +439,7 @@ Function New-ExtractDataFromSDDCBackup { $sddcManagerObject += [pscustomobject]@{ 'fqdn' = $sddcManagerFqdn 'vmname' = $sddcManagerVmName - 'ip' = $sddcManagerIp + 'ip' = (Resolve-DnsName $sddcManagerFqdn).IPAddress 'fips_enabled' = $metadataJSON.fips_enabled 'ceip_enabled' = $ceipStatus 'version' = $sddcManagerVersion @@ -475,7 +487,7 @@ Function New-ExtractDataFromSDDCBackup { $hostId = $lineContent.split("`t")[0] $gateway = $lineContent.split("`t")[7] $hostName = $lineContent.split("`t")[9] - $hostMgmtIp = $lineContent.split("`t")[10] + $hostMgmtIp = (Resolve-DnsName $lineContent.split("`t")[9]).IPAddress $hostMask = $lineContent.split("`t")[17] $hostVersion = $lineContent.split("`t")[18] $hostVmotionIp = $lineContent.split("`t")[19] @@ -558,10 +570,11 @@ Function New-ExtractDataFromSDDCBackup { $vCenterFqdn = $lineContent.split("`t")[11] $vCenterIp = $lineContent.split("`t")[12] $vCenterVMname = $lineContent.split("`t")[13] - } else { + } + else { $vCenterVersion = $lineContent.split("`t")[9] $vCenterFqdn = $lineContent.split("`t")[10] - $vCenterIp = $lineContent.split("`t")[11] + $vCenterIp = (Resolve-DnsName $vCenterFqdn).IPAddress $vCenterVMname = $lineContent.split("`t")[12] } $vCenterDomainID = ($hostsAndDomains | Where-Object { $_.hostId -eq (($hostsandVcenters | Where-Object { $_.vCenterID -eq $vCenterID })[0].hostID) }).domainID @@ -630,9 +643,11 @@ Function New-ExtractDataFromSDDCBackup { $niocs = $lineContent.split("`t")[5] | ConvertFrom-Json If ($lineContent.split("`t")[6] -ne '\N') { $vdsPortgroups = $lineContent.split("`t")[6] | ConvertFrom-Json - } else { + } + else { $vdsPortgroups = $null } + $sourceID = $lineContent.split("`t")[10] $version = $lineContent.split("`t")[8] $virtualDistributedSwitch = [pscustomobject]@{ @@ -642,6 +657,7 @@ Function New-ExtractDataFromSDDCBackup { 'Name' = $vdsName 'PortGroups' = $vdsPortgroups 'version' = $version + 'sourceID' = $sourceID } If ($lineContent.split("`t")[11] -ne '\N') { @@ -649,7 +665,8 @@ Function New-ExtractDataFromSDDCBackup { $transportZoneContent = $overlayContent.transportZones If ($overlayContent.hostSwitchOperationalMode -ne $null) { $hostSwitchOperationalModeContent = $overlayContent.hostSwitchOperationalMode - } else { + } + else { $hostSwitchOperationalModeContent = 'STANDARD' } @@ -765,7 +782,7 @@ Function New-ExtractDataFromSDDCBackup { $ftt = $lineContent.split("`t")[4] $isDefault = $lineContent.split("`t")[5] $isStretched = $lineContent.split("`t")[6] - $name = $lineContent.split("`t")[7] + #$name = $lineContent.split("`t")[7] $vCenterID = $lineContent.split("`t")[9] $primaryDatastoreName = $lineContent.split("`t")[12] $primaryDatastoreType = $lineContent.split("`t")[13] @@ -816,6 +833,8 @@ Function New-ExtractDataFromSDDCBackup { $vdsObject | Add-Member -NotePropertyName 'niocSpecs' -NotePropertyValue $niocSpecsObject $vdsObject | Add-Member -NotePropertyName 'portgroups' -NotePropertyValue $virtualDistributedSwitchDetails.portgroups $vdsObject | Add-Member -NotePropertyName 'dvsName' -NotePropertyValue $virtualDistributedSwitchDetails.name + $vdsObject | Add-Member -NotePropertyName 'id' -NotePropertyValue $vds.vdsId + $vdsObject | Add-Member -NotePropertyName 'sourceID' -NotePropertyValue $virtualDistributedSwitchDetails.sourceID $vdsObject | Add-Member -NotePropertyName 'vmnics' -NotePropertyValue $null $vdsObject | Add-Member -NotePropertyName 'networks' -NotePropertyValue ("VM_MANAGEMENT", "MANAGEMENT", "VSAN", "VMOTION" | Where-Object { $_ -in $virtualDistributedSwitchDetails.portgroups.transportType }) If ($virtualDistributedSwitchDetails.transportZones) { @@ -980,7 +999,8 @@ Function New-ExtractDataFromSDDCBackup { $vCenter = $vCenters | Where-Object { $_.vCenterDomainID -eq $domainId } If ($sddcManagerObject.version -like "4.4.*") { $ssoDomain = ($pscs | Where-Object { $_.id -eq (($vCentersAndPscs | Where-Object { $_.vcenterId -eq $vcenter.vCenterID }).pscId) }).ssoDomain - } else { + } + else { $ssoDomain = $lineContent.split("`t")[11] } $vCenterDetails = [pscustomobject]@{ @@ -1020,12 +1040,13 @@ Function New-ExtractDataFromSDDCBackup { $nsxClusterDetailsObject | Add-Member -NotePropertyName 'clusterVip' -NotePropertyValue ($nsxtManagerClusters | Where-Object { $_.domainIDs -contains $domainId }).clusterVip $nsxClusterDetailsObject | Add-Member -NotePropertyName 'clusterFqdn' -NotePropertyValue ($nsxtManagerClusters | Where-Object { $_.domainIDs -contains $domainId }).clusterFqdn $nsxClusterDetailsObject | Add-Member -NotePropertyName 'rootNsxtManagerPassword' -NotePropertyValue ($passwordVaultObject | Where-Object { ($_.entityName -eq ($nsxtManagerClusters | Where-Object { $_.domainIDs -contains $domainId }).clusterFqdn) -and ($_.credentialType -eq 'SSH') }).password - $nsxClusterDetailsObject | Add-Member -NotePropertyName 'nsxtAdminPassword' -NotePropertyValue ($passwordVaultObject | Where-Object { ($_.entityName -eq ($nsxtManagerClusters | Where-Object { $_.domainIDs -contains $domainId }).clusterFqdn) -and ($_.credentialType -eq 'API') }).password + $nsxClusterDetailsObject | Add-Member -NotePropertyName 'nsxtAdminPassword' -NotePropertyValue ($passwordVaultObject | Where-Object { ($_.entityName -eq ($nsxtManagerClusters | Where-Object { $_.domainIDs -contains $domainId }).clusterFqdn) -and ($_.credentialType -eq 'API') -and ($_.username -eq 'admin') }).password $nsxClusterDetailsObject | Add-Member -NotePropertyName 'nsxtAuditPassword' -NotePropertyValue ($passwordVaultObject | Where-Object { ($_.entityName -eq ($nsxtManagerClusters | Where-Object { $_.domainIDs -contains $domainId }).clusterFqdn) -and ($_.credentialType -eq 'AUDIT') }).password If (($licenseModels | Where-Object { $_.resourceId -eq $domainID }).licensingMode) { $licenseModel = ($licenseModels | Where-Object { $_.resourceId -eq $domainID }).licensingMode - } else { + } + else { $licenseModel = 'PERPETUAL' } $workloadDomains += [pscustomobject]@{ @@ -1055,15 +1076,88 @@ Function New-ExtractDataFromSDDCBackup { $sddcDataObject | ConvertTo-Json -Depth 10 | Out-File "$parentFolder\extracted-sddc-data.json" #Cleanup - LogMessage -type INFO -message "[$jumpboxName] Cleaning up extracted files" + <# LogMessage -type INFO -message "[$jumpboxName] Cleaning up extracted files" Remove-Item -Path "$parentFolder\decrypted-sddc-manager-backup.tar.gz" -force -confirm:$false Remove-Item -Path "$parentFolder\decrypted-sddc-manager-backup.tar" -force -confirm:$false - Remove-Item -path "$parentFolder\$extractedBackupFolder" -Recurse + Remove-Item -path "$parentFolder\$extractedBackupFolder" -Recurse #> LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)" } Export-ModuleMember -Function New-ExtractDataFromSDDCBackup +Function Update-ExtractdDataFromSDDCBackup { + <# + .SYNOPSIS + Updates extracted SDDC Data JSON file with detail not caprured in the SDDC manager backup VCF Instance Recovery. + + .DESCRIPTION + The Update-ExtractdDataFromSDDCBackup cmdlet Updates extracted SDDC Data JSON file with detail not caprured in the SDDC manager backup VCF Instance Recovery. + + .EXAMPLE + Update-ExtractdDataFromSDDCBackup -extractedSDDCDataFile "".\extracted-sddc-data.json" -sddcManagerFQDN "sfo-vcf01.sfo.rainpole.io" -sddcManagerAdmin "administrator@vsphere.local" -sddcManagerAdminPassword "VMw@re1!VMw@re1!" + + .PARAMETER extractedSDDCDataFile + Relative or absolute to the extracted-sddc-data.json file (previously created by New-ExtractDataFromSDDCBackup) somewhere on the local filesystem + + .PARAMETER sddcManagerFQDN + FQDN of the SDDC Manager that should be queried + + .PARAMETER sddcManagerAdminUser + Admin username for SDDC Manager + + .PARAMETER sddcManagerAdminUserPassword + Password for the admin user on SDDC Manager + #> + + Param( + [Parameter (Mandatory = $true)][String] $extractedSDDCDataFile, + [Parameter (Mandatory = $true)][String] $sddcManagerFQDN, + [Parameter (Mandatory = $true)][String] $sddcManagerAdmin, + [Parameter (Mandatory = $true)][String] $sddcManagerAdminPassword + ) + $jumpboxName = hostname + LogMessage -type NOTE -message "[$jumpboxName] Starting Task $($MyInvocation.MyCommand)" + LogMessage -type INFO -message "[$jumpboxName] Reading Extracted Data" + $extractedDataFilePath = (Resolve-Path -Path $extractedSDDCDataFile).path + $extractedSddcData = Get-Content $extractedDataFilePath | ConvertFrom-JSON + $sddcManagerConnection = Connect-VcfSddcManagerServer -server $sddcManagerFQDN -User $sddcManagerAdmin -Password $sddcManagerAdminPassword + + Foreach ($workloadDomain in $extractedSddcData.workloadDomains) { + Foreach ($cluster in $extractedSddcData.workloadDomains.vsphereClusterDetails) { + $clusterName = (Invoke-VcfGetCluster -Id $cluster.id).Name + $cluster.name = $clusterName + + Foreach ($vds in $cluster.vdsDetails) { + $vdsName = (Invoke-VcfGetVdses -ClusterId $cluster.id | Where-Object {$_.id -eq $vds.id}).Name + $vds.dvsName = $vdsName + + } + + Foreach ($portGroup in $vds.PortGroups) { + if ($portGroup.TransportType -eq "VM_MANAGEMENT") { + $vmManagementPGName = ((Invoke-VcfGetVdses -ClusterId $cluster.id).PortGroups | Where-Object {$_.TransportType -eq "VM_MANAGEMENT"}).Name + $portGroup | Add-Member -NotePropertyName "Name" -NotePropertyValue $vmManagementPGName -Force + } + if ($portGroup.TransportType -eq "MANAGEMENT") { + $managementPGName = ((Invoke-VcfGetVdses -ClusterId $cluster.id).PortGroups | Where-Object {$_.TransportType -eq "MANAGEMENT"}).Name + $portGroup | Add-Member -NotePropertyName "Name" -NotePropertyValue $managementPGName -Force + } + if ($portGroup.TransportType -eq "VMOTION") { + $vMotionPGName = ((Invoke-VcfGetVdses -ClusterId $cluster.id).PortGroups | Where-Object {$_.TransportType -eq "VMOTION"}).Name + $portGroup | Add-Member -NotePropertyName "Name" -NotePropertyValue $vMotionPGName -Force + } + if ($portGroup.TransportType -eq "VSAN") { + $vSanPGName = ((Invoke-VcfGetVdses -ClusterId $cluster.id).PortGroups | Where-Object {$_.TransportType -eq "VSAN"}).Name + $portGroup | Add-Member -NotePropertyName "Name" -NotePropertyValue $vSanPGName -Force + } + + } + } + } + LogMessage -type INFO -message "[$jumpboxName] Updating Extracted Data" + $extractedSddcData | ConvertTo-Json -Depth 20 | Out-File $extractedSDDCDataFile +} +Export-ModuleMember -Function Update-ExtractdDataFromSDDCBackup Function New-PrepareforPartialBringup { <# .SYNOPSIS @@ -1073,7 +1167,7 @@ Function New-PrepareforPartialBringup { The New-PrepareforPartialBringup cmdlet prepares a running Cloud Builder system to perform a partial VCF bringup suitable for VCF Instance Recovery. .EXAMPLE - New-PrepareforPartialBringup "-extractedSDDCDataFile .\extracted-sddc-data.json" -cloudBuilderFQDN "sfo-cb01.sfo.rainpole.io" -cloudBuilderAdminUserPassword "VMw@re1!" -cloudBuilderRootUserPassword "VMw@re1!" + New-PrepareforPartialBringup -extractedSDDCDataFile ".\extracted-sddc-data.json" -cloudBuilderFQDN "sfo-cb01.sfo.rainpole.io" -cloudBuilderAdminUserPassword "VMw@re1!" -cloudBuilderRootUserPassword "VMw@re1!" .PARAMETER extractedSDDCDataFile Relative or absolute to the extracted-sddc-data.json file (previously created by New-ExtractDataFromSDDCBackup) somewhere on the local filesystem @@ -1232,7 +1326,8 @@ Function New-ReconstructedPartialBringupJsonSpec { } Until (($esxiLicenseSelection -in $esxiLicensesDisplayObject.ID) -OR ($esxiLicenseSelection -eq "c")) If ($esxiLicenseSelection -eq "c") { Break } $esxiLicenseToUse = ($esxiLicensesDisplayObject | Where-Object { $_.id -eq $esxiLicenseSelection }).license - } else { + } + else { $esxiLicenseToUse = $esxiLicenseKeys[0] } @@ -1308,7 +1403,8 @@ Function New-ReconstructedPartialBringupJsonSpec { Write-Host ""; $remainingNicsDisplayObject | format-table -Property @{Expression = " " }, id, deviceName, driver, linkStatus, description -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r", "`n") } If ($primaryCluster.vdsDetails[$vdsConfigurationIndex].transportZones) { $networksDisplay = ($primaryCluster.vdsDetails[$vdsConfigurationIndex].networks += "OVERLAY") -join (",") - } else { + } + else { $networksDisplay = $primaryCluster.vdsDetails[$vdsConfigurationIndex].networks -join (",") } Write-Host ""; Write-Host " Recreating " -ForegroundColor Yellow -nonewline; Write-Host "$($primaryCluster.vdsDetails[$vdsConfigurationIndex].dvsName)" -ForegroundColor cyan -nonewline; Write-Host " which contained the networks: " -ForegroundColor Yellow -nonewline; Write-Host "$networksDisplay" -ForegroundColor Cyan @@ -1513,7 +1609,8 @@ Function New-ReconstructedPartialBringupJsonSpec { } Until (($nsxLicenseSelection -in $nsxLicensesDisplayObject.ID) -OR ($nsxLicenseSelection -eq "c")) If ($nsxLicenseSelection -eq "c") { Break } $nsxLicenseToUse = ($nsxLicensesDisplayObject | Where-Object { $_.id -eq $nsxLicenseSelection }).license - } else { + } + else { $nsxLicenseToUse = $nsxLicenseKeys[0] } @@ -1565,7 +1662,8 @@ Function New-ReconstructedPartialBringupJsonSpec { } Until (($vsanLicenseSelection -in $vsanLicensesDisplayObject.ID) -OR ($vsanLicenseSelection -eq "c")) If ($vsanLicenseSelection -eq "c") { Break } $vsanLicenseToUse = ($vsanLicensesDisplayObject | Where-Object { $_.id -eq $vsanLicenseSelection }).license - } else { + } + else { $vsanLicenseToUse = $vsanLicenseKeys[0] } @@ -1655,7 +1753,8 @@ Function New-ReconstructedPartialBringupJsonSpec { } Until (($vCenterLicenseSelection -in $vCenterLicensesDisplayObject.ID) -OR ($vCenterLicenseSelection -eq "c")) If ($vCenterLicenseSelection -eq "c") { Break } $vCenterLicenseToUse = ($vCenterLicensesDisplayObject | Where-Object { $_.id -eq $vCenterLicenseSelection }).license - } else { + } + else { $vCenterLicenseToUse = $vCenterLicenseKeys[0] } @@ -1676,12 +1775,407 @@ Function New-ReconstructedPartialBringupJsonSpec { LogMessage -type INFO -message "[$jumpboxName] Saving partial bringup JSON spec: $(($extractedSddcData.workloadDomains | Where-Object {$_.domainType -eq "MANAGEMENT"}).domainName + "-partial-bringup-spec.json")" ConvertTo-Json $mgmtDomainObject -depth 10 | Out-File (($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).domainName + "-partial-bringup-spec.json") LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)" - } else { + } + else { LogMessage -type WARNING -message "[$jumpboxName] Aborted Task $($MyInvocation.MyCommand)" } } Export-ModuleMember -Function New-ReconstructedPartialBringupJsonSpec +Function New-VVFBasedPartialBringupJsonSpec { + + Param( + [Parameter (Mandatory = $true)][String] $tempVcenterIp, + [Parameter (Mandatory = $true)][String] $tempVcenterFqdn, + [Parameter (Mandatory = $true)][String] $extractedSDDCDataFile, + [Parameter (Mandatory = $true)][String] $transportVlanId, + [Parameter (Mandatory = $true)][boolean] $dedupEnabled, + [Parameter (Mandatory = $true)][String] $vcenterServerSize + ) + + $jumpboxName = hostname + LogMessage -type NOTE -message "[$jumpboxName] Starting Task $($MyInvocation.MyCommand)" + LogMessage -type INFO -message "[$jumpboxName] Reading Extracted Data" + + $extractedDataFilePath = (Resolve-Path -Path $extractedSDDCDataFile).path + $extractedSddcData = Get-Content $extractedDataFilePath | ConvertFrom-JSON + + $managementDomain = $extractedSddcData.workloadDomains | Where-Object { $_.domaintype -eq "MANAGEMENT" } + $domainName = $managementDomain.domainName + $defaultManagementcluster = $managementDomain.vsphereClusterDetails | Where-Object { $_.isDefault -eq 't' } + $defaultManagementclusterDistributedSwitches = $defaultManagementcluster.vdsDetails + + $module = "Management Domain JSON Spec" + LogMessage -Type INFO -Message "Generating the $module" + + #dnsSpec + If ($extractedSddcData.mgmtDomainInfrastructure.secondaryDnsServer -eq "n/a") { + [Array]$nameServers = $extractedSddcData.mgmtDomainInfrastructure.primaryDnsServer + } else { + [Array]$nameServers = $extractedSddcData.mgmtDomainInfrastructure.primaryDnsServer, $extractedSddcData.mgmtDomainInfrastructure.secondaryDnsServer + } + $dnsObject = @() + $dnsObject += [pscustomobject]@{ + 'nameservers' = $nameServers + 'subdomain' = $tempVcenterFqdn.split(".", 2)[1] + } + + #ntpServers + If ($extractedSddcData.mgmtDomainInfrastructure.ntpServers[1] -eq "n/a") { + [Array]$ntpServers = $extractedSddcData.mgmtDomainInfrastructure.ntpServers[0] + } else { + [Array]$ntpServers = $extractedSddcData.mgmtDomainInfrastructure.ntpServers[0], $extractedSddcData.mgmtDomainInfrastructure.ntpServers[1] + } + + #vcenterSpec + $vcenterObject = @() + $vcenterObject += [pscustomobject]@{ + 'vcenterHostname' = $tempVcenterFqdn + 'rootVcenterPassword' = ($extractedSddcData.passwords | Where-Object { ($_.domainName -eq $managementDomain.domainName) -and ($_.entityType -eq "VCENTER") -and ($_.username -eq "root") }).password + 'adminUserSsoPassword' = ($extractedSddcData.passwords | Where-Object { ($_.domainName -eq $managementDomain.domainName) -and ($_.entityType -eq "PSC") }).password + 'vmSize' = $vcenterServerSize + 'storageSize' = 'lstorage' + 'ssoDomain' = ($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).ssoDomain + 'useExistingDeployment' = $false + } + + #clusterSpec + $clusterObject = @() + $clusterObject += [pscustomobject]@{ + 'clusterName' = 'vcfir-cl01' + 'datacenterName' = 'vcfir-dc01' + } + + #vsanSpec + If ($defaultManagementcluster.primaryDatastoreType -eq "VSAN_ESA") { + $ESAenabledtrueobject = @() + $ESAenabledtrueobject += [pscustomobject]@{ + 'enabled' = $true + } + } elseIf ($defaultManagementcluster.primaryDatastoreType -eq "VSAN_OSA") { + $ESAenabledtrueobject = @() + $ESAenabledtrueobject += [pscustomobject]@{ + 'enabled' = $false + } + } + $vsanObject = @() + $vsanObject += [pscustomobject]@{ + 'datastoreName' = $extractedSddcData.mgmtDomainInfrastructure.vsan_datastore + 'esaConfig' = ($ESAenabledtrueobject | Select-Object -Skip 0) + } + If ($defaultManagementcluster.primaryDatastoreType -eq "VSAN-OSA") { + $vsanObject | Add-Member -NotePropertyName 'vsanDedup' -NotePropertyValue $dedupEnabled + } + $datastoreSpecObject = @() + $datastoreSpecObject += [pscustomobject]@{ + 'vsanSpec' = ($vsanObject | Select-Object -Skip 0) + } + + #hostSpecsObject + $mgmtHosts = ($extractedSddcData.passwords | where-object { ($_.domainName -eq $domainName) -and ($_.entityType -eq "ESXI") -and ($_.username -eq "root") -and ($_.entityName -in $defaultManagementcluster.hosts.hostname) }) | Sort-Object -Property entityName + $hostSpecsObject = @() + Foreach ($mgmtHost in $mgmtHosts) { + $hostCredentialsObject = @() + $hostCredentialsObject += [pscustomobject]@{ + 'username' = $mgmtHost.Username + 'password' = $mgmtHost.Password + } + + If ([System.Environment]::OSVersion.Platform -eq 'Win32NT') { + $sslThumbPrint = (echo "Q" | openssl.exe s_client -connect "$($mgmtHost.entityName):443" -showcerts 2>$null | Filter-X509 | openssl.exe x509 -noout -fingerprint -sha256).split("sha256 Fingerprint=")[1] + } else { + $sslThumbPrint = (echo "Q" | openssl s_client -connect "$($mgmtHost.entityName):443" -showcerts 2>$null | Filter-X509 | openssl x509 -noout -fingerprint -sha256).split("sha256 Fingerprint=")[1] + } + $hostSpecsObject += [pscustomobject]@{ + 'hostname' = $mgmtHost.entityName + 'credentials' = ($hostCredentialsObject | Select-Object -Skip 0) + 'sslThumbPrint' = $sslThumbPrint + } + } + + #networkSpecs + $vmotionIpObject = @() + $vmotionIpObject += [pscustomobject]@{ + 'startIpAddress' = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VMOTION' }).startIPAddress + 'endIpAddress' = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VMOTION' }).endIpAddress + } + $vsanIpObject = @() + $vsanIpObject += [pscustomobject]@{ + 'startIpAddress' = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VSAN' }).startIPAddress + 'endIpAddress' = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VSAN' }).endIpAddress + } + + $activeUplinksArray = @() + $activeUplinksArray += "uplink1" + $activeUplinksArray += "uplink2" + + $networkSpecsObject = @() + + #VM_MANAGEMENT network + [IPAddress] $ip = $extractedSddcData.mgmtDomainInfrastructure.netmask + $octets = $ip.IPAddressToString.Split('.') + Foreach ($octet in $octets) { $binary = [System.Convert]::ToString([int]$octet, 2).PadLeft(8, '0'); $managementVmNetworkCidr += ($binary.ToCharArray() | Where-Object { $_ -eq '1' }).Count } + $managementVmNetworkSubnet = ($extractedSddcData.mgmtDomainInfrastructure.subnet + "/" + $managementVmNetworkCidr) + $networkSpecsObject += [pscustomobject]@{ + 'networkType' = "VM_MANAGEMENT" + 'subnet' = $managementVmNetworkSubnet + 'gateway' = $extractedSddcData.mgmtDomainInfrastructure.gateway + 'subnetMask' = $null + 'includeIpAddress' = $null + 'includeIpAddressRanges' = $null + 'vlanId' = ($defaultManagementCluster.vdsDetails.portgroups | Where-Object { $_.transportType -eq 'VM_MANAGEMENT' }).vlanId -as [string] + 'mtu' = "1500" + 'teamingPolicy' = 'loadbalance_loadbased' + 'activeUplinks' = $activeUplinksArray + 'standbyUplinks' = $null + 'portGroupKey' = "vcfir-cl01-vds01-pg-vm-mgmt" + } + + #MANAGEMENT network + [IPAddress] $ip = (($defaultManagementCluster | Where-Object { $_.isDefault -eq 't' }).hosts[0].networks | Where-Object { $_.type -eq 'MANAGEMENT' }).mask + $octets = $ip.IPAddressToString.Split('.') + Foreach ($octet in $octets) { $binary = [System.Convert]::ToString([int]$octet, 2).PadLeft(8, '0'); $managementNetworkCidr += ($binary.ToCharArray() | Where-Object { $_ -eq '1' }).Count } + $managementNetworkSubnet = ((($defaultManagementCluster | Where-Object { $_.isDefault -eq 't' }).hosts[0].networks | Where-Object { $_.type -eq 'MANAGEMENT' }).subnet + "/" + $managementNetworkCidr) + $networkSpecsObject += [pscustomobject]@{ + 'networkType' = "MANAGEMENT" + 'subnet' = $managementNetworkSubnet + 'gateway' = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'MANAGEMENT' }).gateway + 'subnetMask' = $null + 'includeIpAddress' = $null + 'includeIpAddressRanges' = $null + 'vlanId' = ($defaultManagementCluster.vdsDetails.portgroups | Where-Object { $_.transportType -eq 'MANAGEMENT' }).vlanId -as [string] + 'mtu' = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'MANAGEMENT' }).mtu -as [string] + 'teamingPolicy' = 'loadbalance_loadbased' + 'activeUplinks' = $activeUplinksArray + 'standbyUplinks' = $null + 'portGroupKey' = "vcfir-cl01-vds01-pg-esx-mgmt" + } + + #VMOTION network + [IPAddress] $ip = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VMOTION' }).subnetMask + $octets = $ip.IPAddressToString.Split('.') + Foreach ($octet in $octets) { $binary = [System.Convert]::ToString([int]$octet, 2).PadLeft(8, '0'); $vmotionNetworkCidr += ($binary.ToCharArray() | Where-Object { $_ -eq '1' }).Count } + $vmotionNetworkSubnet = (($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VMOTION' }).subnet + "/" + $vmotionNetworkCidr) + $networkSpecsObject += [pscustomobject]@{ + 'networkType' = "VMOTION" + 'subnet' = $vmotionNetworkSubnet + 'gateway' = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VMOTION' }).gateway + 'subnetMask' = $null + 'includeIpAddress' = $null + 'includeIpAddressRanges' = $vmotionIpObject + 'vlanId' = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VMOTION' }).vlanId -as [string] + 'mtu' = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VMOTION' }).mtu -as [string] + 'teamingPolicy' = 'loadbalance_loadbased' + 'activeUplinks' = $activeUplinksArray + 'standbyUplinks' = $null + 'portGroupKey' = "vcfir-cl01-vds01-pg-vmotion" + } + + #VSAN network + [IPAddress] $ip = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VSAN' }).subnetMask + $octets = $ip.IPAddressToString.Split('.') + Foreach ($octet in $octets) { $binary = [System.Convert]::ToString([int]$octet, 2).PadLeft(8, '0'); $vsanNetworkCidr += ($binary.ToCharArray() | Where-Object { $_ -eq '1' }).Count } + $vsanNetworkSubnet = (($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VSAN' }).subnet + "/" + $vmotionNetworkCidr) + $networkSpecsObject += [pscustomobject]@{ + 'networkType' = "VSAN" + 'subnet' = $vsanNetworkSubnet + 'gateway' = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VSAN' }).gateway + 'subnetMask' = $null + 'includeIpAddress' = $null + 'includeIpAddressRanges' = $vsanIpObject + 'vlanId' = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VSAN' }).vlanId -as [string] + 'mtu' = ($defaultManagementCluster.hosts[0].networks | Where-Object { $_.type -eq 'VSAN' }).mtu -as [string] + 'teamingPolicy' = 'loadbalance_loadbased' + 'activeUplinks' = $activeUplinksArray + 'standbyUplinks' = $null + 'portGroupKey' = "vcfir-cl01-vds01-pg-vm-vsan" + } + + #dvsSpecsObject + $hostConnection = Connect-ViServer $($mgmtHosts[0].entityName) -user $hostSpecsObject[0].credentials.username -password $hostSpecsObject[0].credentials.password + $nics = (Get-EsxCli -VMHost $($mgmtHosts[0].entityName)).network.nic.list() | Select-Object Name, Driver, LinkStatus, Description + + $nicsDisplayObject = @() + $nicsIndex = 1 + $nicsDisplayObject += [pscustomobject]@{ + 'ID' = "ID" + 'deviceName' = "Device Name" + 'driver' = "Driver" + 'linkStatus' = "Link Status" + 'description' = "Description" + } + $nicsDisplayObject += [pscustomobject]@{ + 'ID' = "--" + 'deviceName' = "-----------" + 'driver' = "----------" + 'linkStatus' = "-----------" + 'description' = "-----------------------------------------------" + } + Foreach ($nic in $nics) { + $nicsDisplayObject += [pscustomobject]@{ + 'ID' = $nicsIndex + 'deviceName' = $nic.name + 'driver' = $nic.driver + 'linkStatus' = $nic.linkStatus + 'description' = $nic.description + } + $nicsIndex++ + } + Write-Host ""; Write-Host " Recreating Virtual Distributed Switches as per previous deployment" -ForegroundColor Yellow + $vdsConfiguration = @() + $remainingNicsDisplayObject = $nicsDisplayObject + + #Loop Through VDS Creation + For ($i = 1; $i -le $defaultManagementclusterDistributedSwitches.count; $i++) { + $vdsConfigurationIndex = ($i - 1) + If ($defaultManagementclusterDistributedSwitches[$vdsConfigurationIndex].networks -ne $null) { + Do { + $nicNamesArray = @() + Write-Host ""; $remainingNicsDisplayObject | format-table -Property @{Expression = " " }, id, deviceName, driver, linkStatus, description -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r", "`n") } + If ($defaultManagementclusterDistributedSwitches[$vdsConfigurationIndex].transportZones) { + $networksDisplay = ($defaultManagementclusterDistributedSwitches[$vdsConfigurationIndex].networks += "OVERLAY") -join (",") + $dvsName = "vcfir-cl01-vds0$($i)-nsx" + } else { + $networksDisplay = $defaultManagementclusterDistributedSwitches[$vdsConfigurationIndex].networks -join (",") + $dvsName = "vcfir-cl01-vds0$($i)" + } + Write-Host ""; Write-Host " Creating " -ForegroundColor Yellow -nonewline; Write-Host $dvsName -ForegroundColor cyan -nonewline; Write-Host " which will contain the networks: " -ForegroundColor Yellow -nonewline; Write-Host "$networksDisplay" -ForegroundColor Cyan + Write-Host " Enter a comma seperated list of IDs to use as vmnics for this VDS, or C to Cancel: " -ForegroundColor Yellow -nonewline + $nicSelection = Read-Host + If ($nicSelection -ne "C") { + $nicSelectionInvalid = $false + $nicArray = $nicSelection -split (",") + Foreach ($nic in $nicArray) { + $nicNamesArray += ($nicsDisplayObject | Where-Object { $_.id -eq $nic }).deviceName + If ($nic -notin $nicsDisplayObject.id) { + $nicSelectionInvalid = $true + } + } + } + } Until (($nicSelectionInvalid -eq $false) -OR ($nicSelection -eq "c")) + If ($nicSelection -eq "c") { Break } + $individualVds = [PSCustomObject]@{ + 'vdsName' = $dvsName + 'nicnames' = $nicNamesArray + 'vdsNetworks' = $defaultManagementclusterDistributedSwitches[$vdsConfigurationIndex].networks + 'portgroups' = $defaultManagementclusterDistributedSwitches[$vdsConfigurationIndex].portgroups + } + $vdsConfiguration += $individualVds + $tempremainingNicsDisplayObject = @() + Foreach ( $displaynic in $remainingNicsDisplayObject) { + If ($displaynic.id -notin $nicArray) { + $tempremainingNicsDisplayObject += $displaynic + } + } + $remainingNicsDisplayObject = $tempremainingNicsDisplayObject + } + } + If (($nicSelection -eq "c") -or ($nicSelection -eq "c")) { Break } + + $proposedConfigDisplayObject = @() + $configIndex = 1 + $proposedConfigDisplayObject += [pscustomobject]@{ + 'vdsName' = "VDS Name" + 'nicnames' = "NIC Names" + 'vdsNetworks' = "VDS Networks" + } + $proposedConfigDisplayObject += [pscustomobject]@{ + 'vdsName' = "----------------------------------------" + 'nicnames' = "---------------" + 'vdsNetworks' = "------------------------------" + } + Foreach ($config in $vdsConfiguration) { + $proposedConfigDisplayObject += [pscustomobject]@{ + 'vdsName' = $config.vdsName + 'nicnames' = $config.nicnames -join (", ") + 'vdsNetworks' = $config.vdsNetworks -join (", ") + } + $configIndex++ + } + Write-Host ""; Write-Host " Proposed VDS Configuration " -ForegroundColor Yellow + Write-Host ""; $proposedConfigDisplayObject | format-table -Property @{Expression = " " }, vdsName, nicnames, vdsNetworks, -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r", "`n") } + Write-Host ""; Write-Host " Do you wish to proceed with the proposed configuration? (Y/N): " -ForegroundColor Yellow -nonewline + $proposedConfigAccepted = Read-Host + $proposedConfigAccepted = $proposedConfigAccepted -replace "`t|`n|`r", "" + + If ($proposedConfigAccepted -ieq "Y") { + #Update Objects in Memory with chosen nics + Foreach ($vds in $defaultManagementclusterDistributedSwitches) { + If ($vds.networks -ne $null) { + $nicsToUse = ($proposedConfigDisplayObject | Where-Object { (($_.vdsNetworks.split(", ")) -join (",")) -eq ($vds.networks -join (",")) }).nicnames + } else { + $nicsToUse = ($proposedConfigDisplayObject | Where-Object { $_.vdsNetworks -eq "OVERLAY" }).nicnames + } + $vds.vmnics = @($nicsToUse -split (", ")) + } + } + + $dvsSpecObject = @() + Foreach ($distributedSwitch in $defaultManagementclusterDistributedSwitches) { + If ($distributedSwitch.networks -ne $null) { + $vmnicObject = @() + $vmnicObject += [pscustomobject]@{ + 'id' = $distributedSwitch.vmnics[0] + 'uplink' = "uplink1" + } + $vmnicObject += [pscustomobject]@{ + 'id' = $distributedSwitch.vmnics[1] + 'uplink' = "uplink2" + } + + If ($distributedSwitch.networks -like "*OVERLAY*") { + $dvsName = ("vcfir-cl01-vds0" + $($defaultManagementclusterDistributedSwitches.indexof($distributedSwitch) + 1)) + "-nsx" + } else { + $dvsName = $("vcfir-cl01-vds0" + $($defaultManagementclusterDistributedSwitches.indexof($distributedSwitch) + 1)) + } + $dvsObject = [pscustomobject]@{ + 'dvsName' = $dvsName + 'mtu' = $distributedSwitch.mtu + 'nsxtSwitchConfig' = $null + 'vmnicsToUplinks' = $vmnicObject + 'nsxTeamings' = $teamingsArray + 'networks' = @($distributedSwitch.networks) + } + $dvsSpecObject += $dvsObject + } + } + + #vcfOperationsSpec + $vcfOpsNodesObject += [pscustomobject]@{ + 'hostname' = 'vcfir-ops01.notrequired.io' + 'rootUserPassword' = 'VMw@re1!VMw@re1!' + 'type' = 'master' + } + $vcfOperationsSpecObject += [pscustomobject]@{ + 'nodes' = $vcfOpsNodesObject + 'adminUserPassword' = 'VMw@re1!VMw@re1!' + 'applianceSize' = 'medium' + 'useExistingDeployment' = $false + } + + $excludedComponentsArray = @("VCF_MANAGEMENT_COMPONENTS_NATIVE_DEPLOYMENT") + $managementDomainObject = New-Object -TypeName psobject + $managementDomainObject | Add-Member -notepropertyname 'excludedComponents' -notepropertyvalue $excludedComponentsArray + $managementDomainObject | Add-Member -notepropertyname 'sddcId' -notepropertyvalue $domainName + $managementDomainObject | Add-Member -notepropertyname 'workflowType' -notepropertyvalue "VVF" + $managementDomainObject | Add-Member -notepropertyname 'version' -notepropertyvalue $extractedSddcData.sddcManager.version.Substring(0, $extractedSddcData.sddcManager.version.LastIndexOf(".")) + $managementDomainObject | Add-Member -notepropertyname 'ceipEnabled' -notepropertyvalue $true + $managementDomainObject | Add-Member -notepropertyname 'dnsSpec' -notepropertyvalue ($dnsObject | Select-Object -Skip 0) + $managementDomainObject | Add-Member -notepropertyname 'ntpServers' -notepropertyvalue $ntpServers + $managementDomainObject | Add-Member -notepropertyname 'vcenterSpec' -notepropertyvalue ($vcenterObject | Select-Object -Skip 0) + $managementDomainObject | Add-Member -notepropertyname 'clusterSpec' -notepropertyvalue ($clusterObject | Select-Object -Skip 0) + $managementDomainObject | Add-Member -notepropertyname 'datastoreSpec' -notepropertyvalue ($datastoreSpecObject | Select-Object -Skip 0) + $managementDomainObject | Add-Member -notepropertyname 'hostSpecs' -notepropertyvalue $hostSpecsObject + $managementDomainObject | Add-Member -notepropertyname 'networkSpecs' -notepropertyvalue $networkSpecsObject + $managementDomainObject | Add-Member -notepropertyname 'dvsSpecs' -notepropertyvalue $dvsSpecObject + $managementDomainObject | Add-Member -notepropertyname 'vcfOperationsSpec' -notepropertyvalue ($vcfOperationsSpecObject | Select-Object -Skip 0) + + LogMessage -type INFO -message "[$jumpboxName] Saving partial bringup JSON spec: $(($extractedSddcData.workloadDomains | Where-Object {$_.domainType -eq "MANAGEMENT"}).domainName + "-partial-bringup-spec.json")" + ConvertTo-Json $managementDomainObject -depth 10 | Out-File (($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).domainName + "-partial-bringup-spec.json") + LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)" +} +Export-ModuleMember -Function New-VVFBasedPartialBringupJsonSpec + Function New-PartialManagementDomainDeployment { Param( [Parameter (Mandatory = $true)][String] $partialBringupSpecFile, @@ -1733,15 +2227,18 @@ Function New-PartialManagementDomainDeployment { If (($failureMessage -like "*is not currently synchronising time with NTP Server*") -OR ($failureMessage -like "*reject or unable to use NTP server*")) { $knownErrorFound = "True" LogMessage -Type WARNING -Message "$($failure.description): $failureMessage" - } else { + } + else { $realErrorFound = "True" LogMessage -Type ERROR -Message "$($failure.description): $failureMessage" } - } else { + } + else { If ($failure -like "*Time Synchronization Validation*") { $knownErrorFound = "True" LogMessage -Type WARNING -Message "$($failure.description)" - } else { + } + else { $realErrorFound = "True" LogMessage -Type ERROR -Message "$($failure.description)" } @@ -1785,10 +2282,12 @@ Function New-PartialManagementDomainDeployment { While ($status.Status -eq "IN_PROGRESS") If ($status.status -eq "COMPLETED_WITH_FAILURE") { LogMessage -Type ERROR -Message "[$cloudBuilderFQDN] Deployment of Management Domain completed with errors. Please consult the UI and troubleshoot" - } else { + } + else { LogMessage -Type INFO -Message "[$cloudBuilderFQDN] Deployment of Management Domain completed successfully" } - } Catch { + } + Catch { $ErrorMessage = $_.Exception.Message LogMessage -Type EXCEPTION -Message "Error was: $ErrorMessage" } @@ -1885,15 +2384,16 @@ Function New-NSXManagerOvaDeployment { $nsxManagerDns = "$($extractedSddcData.mgmtDomainInfrastructure.primaryDnsServer),$($extractedSddcData.mgmtDomainInfrastructure.secondaryDnsServer)" $nsxManagerDnsDomain = $extractedSddcData.mgmtDomainInfrastructure.domain $nsxManagerNtpServer = $extractedSddcData.mgmtDomainInfrastructure.ntpServers -join (",") - $nsxManagerAdminUsername = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.credentialType -eq "API") }).username - $nsxManagerAdminPassword = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.credentialType -eq "API") }).password - $nsxManagerCliPassword = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.credentialType -eq "API") }).password + $nsxManagerAdminUsername = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.username -eq "admin") }).username + $nsxManagerAdminPassword = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.username -eq "admin") }).password + $nsxManagerCliPassword = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.username -eq "admin") }).password $nsxManagerCliAuditUsername = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.credentialType -eq "AUDIT") }).username $nsxManagerCliAuditPassword = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.credentialType -eq "AUDIT") }).password If ($nsxManagerCliAuditUsername) { $command = '"C:\Program Files\VMware\VMware OVF Tool\ovftool.exe" --noSSLVerify --acceptAllEulas --allowExtraConfig --diskMode=thin --X:injectOvfEnv --X:logFile=ovftool.log --powerOn --X:waitForIp --name="' + $nsxManagerVMName + '" --datastore="' + $vmDatastore + '" --deploymentOption="' + $restoredNsxManagerDeploymentSize + '" --network="' + $vmNetwork + '" --prop:nsx_role="NSX Manager" --prop:nsx_ip_0="' + $nsxManagerIp + '" --prop:nsx_netmask_0="' + $nsxManagerNetworkMask + '" --prop:nsx_gateway_0="' + $nsxManagerGateway + '" --prop:nsx_dns1_0="' + $nsxManagerDns + '" --prop:nsx_domain_0="' + $nsxManagerDnsDomain + '" --prop:nsx_ntp_0="' + $nsxManagerNtpServer + '" --prop:nsx_isSSHEnabled=True --prop:nsx_allowSSHRootLogin=True --prop:nsx_passwd_0="' + $nsxManagerAdminPassword + '" --prop:nsx_cli_username="' + $nsxManagerAdminUsername + '" --prop:nsx_cli_passwd_0="' + $nsxManagerCliPassword + '" --prop:nsx_cli_audit_passwd_0="' + $nsxManagerCliAuditPassword + '" --prop:nsx_cli_audit_username="' + $nsxManagerCliAuditUsername + '" --prop:nsx_hostname="' + $nsxManagerHostName + '" "' + $nsxManagerOvaFile + '" ' + '"vi://' + $vCenterAdmin + ':' + $vCenterAdminPassword + '@' + $vCenterFqdn + '/' + $datacenterName + '/host/' + $clusterName + '/"' - } else { + } + else { $command = '"C:\Program Files\VMware\VMware OVF Tool\ovftool.exe" --noSSLVerify --acceptAllEulas --allowExtraConfig --diskMode=thin --X:injectOvfEnv --X:logFile=ovftool.log --powerOn --X:waitForIp --name="' + $nsxManagerVMName + '" --datastore="' + $vmDatastore + '" --deploymentOption="' + $restoredNsxManagerDeploymentSize + '" --network="' + $vmNetwork + '" --prop:nsx_role="NSX Manager" --prop:nsx_ip_0="' + $nsxManagerIp + '" --prop:nsx_netmask_0="' + $nsxManagerNetworkMask + '" --prop:nsx_gateway_0="' + $nsxManagerGateway + '" --prop:nsx_dns1_0="' + $nsxManagerDns + '" --prop:nsx_domain_0="' + $nsxManagerDnsDomain + '" --prop:nsx_ntp_0="' + $nsxManagerNtpServer + '" --prop:nsx_isSSHEnabled=True --prop:nsx_allowSSHRootLogin=True --prop:nsx_passwd_0="' + $nsxManagerAdminPassword + '" --prop:nsx_cli_username="' + $nsxManagerAdminUsername + '" --prop:nsx_cli_passwd_0="' + $nsxManagerCliPassword + '" --prop:nsx_hostname="' + $nsxManagerHostName + '" "' + $nsxManagerOvaFile + '" ' + '"vi://' + $vCenterAdmin + ':' + $vCenterAdminPassword + '@' + $vCenterFqdn + '/' + $datacenterName + '/host/' + $clusterName + '/"' <# Action when all if and elseif conditions are false #> } LogMessage -type INFO -message "[$jumpboxName] Deploying NSX Manager OVA" @@ -1915,7 +2415,8 @@ Function New-NSXManagerOvaDeployment { LogMessage -type INFO -message "[$jumpboxName] $line" } } - } else { + } + else { LogMessage -type INFO -message "[$jumpboxName] $($progress[-1])" } } @@ -2009,7 +2510,8 @@ Function New-vCenterOvaDeployment { LogMessage -type INFO -message "[$jumpboxName] $line" } } - } else { + } + else { LogMessage -type INFO -message "[$jumpboxName] $($progress[-1])" } } @@ -2113,7 +2615,8 @@ Function New-SDDCManagerOvaDeployment { LogMessage -type INFO -message "[$jumpboxName] $line" } } - } else { + } + else { LogMessage -type INFO -message "[$jumpboxName] $($progress[-1])" } } @@ -2312,7 +2815,8 @@ Function Invoke-SDDCManagerRestore { $modulePath = (Get-InstalledModule -Name VMware.CloudFoundation.InstanceRecovery).InstalledLocation If ($extractedSddcData.sddcManager.version.replace(".", "").substring(0, 3) -gt "451") { $sourceFile = "$modulePath\reference-files\new_restore_status.json" - } else { + } + else { $sourceFile = "$modulePath\reference-files\old_restore_status.json" } @@ -2364,10 +2868,12 @@ Function Invoke-SDDCManagerRestore { $restoreProgress = ((Invoke-SSHCommand -timeout 30 -sessionid $sshSession.SessionId -command $scriptText).output | ConvertFrom-JSON).status LogMessage -type INFO -message "[$extractedSddcManagerFqdn] Restore Status: $restoreProgress" } While ($restoreProgress -in "IN PROGRESS") - } else { + } + else { LogMessage -type ERROR -message "[$extractedSddcManagerFqdn] Restore Task ID not returned" } - } else { + } + else { LogMessage -type ERROR -message "[$extractedSddcManagerFqdn] Failed to get SDDC Manager Token" } @@ -2446,7 +2952,8 @@ Function Resolve-PhysicalHostServiceAccounts { LogMessage -type INFO -message "[$($hostInstance.name)] VCF Service Account Not Found: Creating" New-VMHostAccount -Id $svcAccountName -Password $svcAccountPassword -Description "ESXi User" | Out-Null New-VIPermission -Entity (Get-Folder root) -Principal $svcAccountName -Role Admin | Out-Null - } else { + } + else { LogMessage -type INFO -message "[$($hostInstance.name)] VCF Service Account Found: Setting Password" Set-VMHostAccount -UserAccount $svcAccountName -Password $svcAccountPassword | Out-Null } @@ -2579,7 +3086,7 @@ Function Invoke-vCenterRestore { Do { $sshSession = New-SSHSession -computername $restoredVcenterFqdn -Credential $mycreds -KnownHost $inmem -erroraction silentlyContinue } Until ($sshSession) - $rpmStatus = (Invoke-SSHCommand -SessionId $sshSession.sessionid -Command "api com.vmware.appliance.version1.services.status.get --name vmbase_init" -erroraction silentlyContinue).output + $rpmStatus = (Invoke-SSHCommand -SessionId $sshSession.sessionid -Command "api com.vmware.appliance.version1.services.status.get --name cap_init" -erroraction silentlyContinue).output } Until ($rpmStatus -eq "Status: down") LogMessage -type INFO -message "[$restoredVcenterFqdn] RPM initialization Complete" @@ -2610,15 +3117,15 @@ Function Invoke-vCenterRestore { $sshSession = New-SSHSession -computername $restoredVcenterFqdn -Credential $mycreds -KnownHost $inmem -erroraction silentlycontinue If ($sshSession) { $stream = New-SSHShellStream -SSHSession $sshSession - $stream.writeline('appliancesh') + <# $stream.writeline('appliancesh') Start-Sleep 5 - $stream.writeline($restoredvCenterRootPassword) + $stream.writeline($restoredvCenterRootPassword) #> Start-Sleep 5 $response = $stream.Read() Start-Sleep 5 $stream.writeline('api com.vmware.appliance.recovery.restore.job.get') Start-Sleep 5 - $restoreStatus = $stream.Read() + $Global:restoreStatus = $stream.Read() $restoreStatusArray = $restoreStatus -split ("\r\n") $state = $restoreStatusArray[2].trim() } @@ -2644,7 +3151,7 @@ Function Invoke-vCenterRestore { If ($restoreStatus) { $restoreStatusArray = $restoreStatus -split ("\r\n") If ($restoreStatusArray) { - If ($restoreStatusArray[2]) { + If ($restoreStatusArray[1]) { $state = $restoreStatusArray[2].trim() } If ($restoreStatusArray[6]) { @@ -2659,7 +3166,8 @@ Function Invoke-vCenterRestore { } Until (($state -eq "State: SUCCEEDED") -or ($state -eq "State: FAILED")) If ($state -eq "State: SUCCEEDED") { LogMessage -type INFO -message "[$restoredVcenterFqdn] Restore finished with $state" - } else { + } + else { LogMessage -type ERROR -message "[$restoredVcenterFqdn] Restore finished with $state" } @@ -2874,17 +3382,13 @@ Function Move-ClusterHostNetworkingTovSS { $extractedDataFilePath = (Resolve-Path -Path $extractedSDDCDataFile).path $extractedSddcData = Get-Content $extractedDataFilePath | ConvertFrom-JSON - $truncatedSddcManagerVersion = $extractedSddcData.sddcManager.version.replace(".", "").substring(0, 2) - If ($truncatedSddcManagerVersion -ge "51") { - $mgmtVmVlanId = ((($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereClusterDetails | Where-Object { $_.name -eq $clusterName }).vdsDetails.portgroups | Where-Object { $_.transportType -eq "VM_MANAGEMENT" }).vlanID - } else { - $mgmtVmVlanId = ((($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereClusterDetails | Where-Object { $_.name -eq $clusterName }).vdsDetails.portgroups | Where-Object { $_.transportType -eq "MANAGEMENT" }).vlanID - } - $mgmtVlanId = ((($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereClusterDetails | Where-Object { $_.name -eq $clusterName }).vdsDetails.portgroups | Where-Object { $_.transportType -eq "MANAGEMENT" }).vlanID - $vMotionVlanId = ((($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereClusterDetails | Where-Object { $_.name -eq $clusterName }).vdsDetails.portgroups | Where-Object { $_.transportType -eq "VMOTION" }).vlanID - $vSanVlanId = ((($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereClusterDetails | Where-Object { $_.name -eq $clusterName }).vdsDetails.portgroups | Where-Object { $_.transportType -eq "VSAN" }).vlanID - #$vdsName = (($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereclusterdetails | Where-Object { $_.isDefault -eq "t" }).vdsdetails.dvsName - $clustervdswitches = ($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereclusterdetails.vdsDetails + $mgmtVmVlanId = ((($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereClusterDetails | Where-Object { $_.isDefault -eq "t" }).vdsDetails.portgroups | Where-Object { $_.transportType -eq "VM_MANAGEMENT" }).vlanID + $mgmtVlanId = ((($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereClusterDetails | Where-Object { $_.isDefault -eq "t" }).vdsDetails.portgroups | Where-Object { $_.transportType -eq "MANAGEMENT" }).vlanID + $vMotionVlanId = ((($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereClusterDetails | Where-Object { $_.isDefault -eq "t" }).vdsDetails.portgroups | Where-Object { $_.transportType -eq "VMOTION" }).vlanID + $vSanVlanId = ((($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereClusterDetails | Where-Object { $_.isDefault -eq "t" }).vdsDetails.portgroups | Where-Object { $_.transportType -eq "VSAN" }).vlanID + $vdsName = (($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereclusterdetails | Where-Object { $_.isDefault -eq "t" }).vdsdetails.dvsName + $clustervdswitches = (($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereclusterdetails | Where-Object { $_.isDefault -eq "t" }).vdsDetails + $clusterMoRef = (($extractedSddcData.workloadDomains | Where-Object { $_.domainType -eq "MANAGEMENT" }).vsphereclusterdetails | Where-Object { $_.isDefault -eq "t" }).sourceID $vss_name = "vSwitch0" $mgmt_name = "Management" @@ -2893,14 +3397,14 @@ Function Move-ClusterHostNetworkingTovSS { LogMessage -type INFO -message "[$vCenterFqdn] Establishing Connection" Connect-VIServer $vCenterFqdn -user $vCenterAdmin -password $vCenterAdminPassword *>$null - $vmhostArray = Get-Cluster -name $clusterName | Get-VMhost | Sort-Object -Property Name + $vmhostArray = Get-Cluster | Get-VMhost | Sort-Object -Property Name $existingAttribute = Get-CustomAttribute -Name vdsConfiguration -TargetType Cluster -erroraction SilentlyContinue If (!$existingAttribute) { New-CustomAttribute -Name vdsConfiguration -TargetType Cluster | Out-Null } - $storedVdsConfiguration = (((Get-Cluster -name $clustername).customfields | Where-Object { $_.key -eq "vdsConfiguration" }).value) | ConvertFrom-Json + $storedVdsConfiguration = (((Get-Cluster).customfields | Where-Object { $_.key -eq "vdsConfiguration" }).value) | ConvertFrom-Json If (!$storedVdsConfiguration) { $clustervdsConfiguration = @() Foreach ($vds in $clustervdswitches) { @@ -2953,7 +3457,8 @@ Function Move-ClusterHostNetworkingTovSS { If ($nicToMoveToVdsFirst -in $nicsInVds) { LogMessage -type INFO -message "[$vmhost] Removing $nicToMoveToVdsFirst from VDS" Get-VMHostNetworkAdapter -VMHost $vmhost -Physical -Name $nicToMoveToVdsFirst | Remove-VDSwitchPhysicalNetworkAdapter -Confirm:$false | Out-Null - } else { + } + else { LogMessage -type INFO -message "[$vmhost] $nicToMoveToVdsFirst already removed from VDS. Skipping" } $vssExists = Get-VMHost -Name $vmhost | Get-VirtualSwitch -Name $vss_name -errorAction silentlyContinue @@ -2961,7 +3466,8 @@ Function Move-ClusterHostNetworkingTovSS { If (!($vssExists)) { LogMessage -type INFO -message "[$vmhost] Creating new VSS" New-VirtualSwitch -VMHost $vmhost -Name $vss_name -mtu $mtu | Out-Null - } else { + } + else { LogMessage -type INFO -message "[$vmhost] VSS already exists. Skipping" } @@ -2970,7 +3476,8 @@ Function Move-ClusterHostNetworkingTovSS { If (!($tempMgmtPgExists)) { LogMessage -type INFO -message "[$vmhost] Creating temporary management portgroup `'mgmt_temp`'" New-VirtualPortGroup -VirtualSwitch (Get-VirtualSwitch -VMHost $vmhost -Name $vss_name) -Name "mgmt_temp" -VLanId $mgmtVmVlanId | Out-Null - } else { + } + else { LogMessage -type INFO -message "[$vmhost] Temporary management portgroup `'mgmt_temp`' already exists. Skipping" } } @@ -2987,7 +3494,8 @@ Function Move-ClusterHostNetworkingTovSS { If (!($mgmtPgExists)) { LogMessage -type INFO -message "[$vmhost] Creating $mgmt_name portrgroup on $vss_name" $mgmt_pg = New-VirtualPortGroup -VirtualSwitch $vss -Name $mgmt_name -VLanId $mgmtVlanId - } else { + } + else { LogMessage -type INFO -message "[$vmhost] Management portgroup $mgmt_name already exists. Skipping" } @@ -2998,7 +3506,8 @@ Function Move-ClusterHostNetworkingTovSS { If (!($vmotionPgExists)) { LogMessage -type INFO -message "[$vmhost] Creating $vmotion_name portrgroup on $vss_name" $vmotion_pg = New-VirtualPortGroup -VirtualSwitch $vss -Name $vmotion_name -VLanId $vMotionVlanId - } else { + } + else { LogMessage -type INFO -message "[$vmhost] Management portgroup $vmotion_name already exists. Skipping" } @@ -3009,7 +3518,8 @@ Function Move-ClusterHostNetworkingTovSS { If (!($storagePgExists)) { LogMessage -type INFO -message "[$vmhost] Creating $storage_name Network portrgroup on $vss_name" $storage_pg = New-VirtualPortGroup -VirtualSwitch $vss -Name $storage_name -VLanId $vSanVlanId - } else { + } + else { LogMessage -type INFO -message "[$vmhost] Management portgroup $storage_name already exists. Skipping" } @@ -3018,7 +3528,8 @@ Function Move-ClusterHostNetworkingTovSS { If ($vss.ExtensionData.Pnic -notlike "*$nicToMoveToVdsFirst") { LogMessage -type INFO -message "[$vmhost] Migrating $nicToMoveToVdsFirst from $vdsName to $vss_name" Add-VirtualSwitchPhysicalNetworkAdapter -VirtualSwitch $vss -VMHostPhysicalNic $vmnicToMove -confirm:$false - } else { + } + else { LogMessage -type INFO -message "[$vmhost] $nicToMoveToVdsFirst already part of VSS. Skipping" } @@ -3030,7 +3541,8 @@ Function Move-ClusterHostNetworkingTovSS { If ($currentStorageVmkPortgroup -ne $storage_name) { LogMessage -type INFO -message "[$vmhost] Migrating VSAN vmKernel from $vdsName to $vss_name" Move-VMKernel -VMHost $vmhost -Interface "vmk2" -NetworkName $storage_name - } else { + } + else { LogMessage -type INFO -message "[$vmhost] VSAN vmKernel already on $vss_name. Skipping" } @@ -3040,7 +3552,8 @@ Function Move-ClusterHostNetworkingTovSS { If ($currentVmotionVmkPortgroup -ne $vmotion_name) { LogMessage -type INFO -message "[$vmhost] Migrating vMotion vmKernel from $vdsName to $vss_name" Move-VMKernel -VMHost $vmhost -Interface "vmk1" -NetworkName $vmotion_name - } else { + } + else { LogMessage -type INFO -message "[$vmhost] vMotion vmKernel already on $vss_name. Skipping" } @@ -3050,7 +3563,8 @@ Function Move-ClusterHostNetworkingTovSS { If ($currentMgmtVmkPortgroup -ne $mgmt_name) { LogMessage -type INFO -message "[$vmhost] Migrating Management vmKernel from $vdsName to $vss_name" Move-VMKernel -VMHost $vmhost -Interface "vmk0" -NetworkName $mgmt_name - } else { + } + else { LogMessage -type INFO -message "[$vmhost] Management vmKernel already on $vss_name. Skipping" } @@ -3171,7 +3685,8 @@ Function Set-ClusterHostsvSanIgnoreClusterMemberList { $esxiHosts = get-cluster -name $clusterName | get-vmhost if ($setting -eq "enable") { $value = 1 - } else { + } + else { $value = 0 } $esxCommand = "esxcli system settings advanced set --int-value=$value --option=/VSAN/IgnoreClusterMemberListUpdates" @@ -3329,7 +3844,8 @@ Function Remove-NonResponsiveHosts { Foreach ($hostID in $hostIDs) { If ($nsxManagerVersion -le "313") { $uri = "https://$nsxManagerFqdn/api/v1/fabric/nodes/$($hostID)?unprepare_host=false" - } else { + } + else { $uri = "https://$nsxManagerFqdn/api/v1/transport-nodes/$($hostID)?force=true&unprepare_host=false" } LogMessage -type INFO -message "[$nsxManagerFqdn] Removing Transport Node associated with $(($allHostTransportNodes | Where-Object {$_.id -eq $hostID}).display_name)" @@ -3353,7 +3869,7 @@ Function Remove-NonResponsiveHosts { Disconnect-VIServer -Server $global:DefaultVIServers -Force -Confirm:$false #If VLCM cluster, wait until cleanup of cluster post TN delete is done - If ($clusterVlcmManaged -eq "true") { + <# If ($clusterVlcmManaged -eq "true") { $SecurePassword = ConvertTo-SecureString -String $nsxManagerRootPassword -AsPlainText -Force $mycreds = New-Object System.Management.Automation.PSCredential ("root", $SecurePassword) $inmem = New-SSHMemoryKnownHost @@ -3369,13 +3885,14 @@ Function Remove-NonResponsiveHosts { $relevantUpdates = (Invoke-SSHCommand -timeout 30 -sessionid $sshSession.SessionId -command $nsxCommand).output } Until ($relevantUpdates[-1] -like "*RemoveNsxVlcmActivity*phase= `'Begin`'*next phase= `'Success!`'") Remove-SSHSession -SSHSession $sshSession | Out-Null - } + } #> #Reattach TNP #Get Transport Node Profiles If ($nsxManagerVersion -le "313") { $uri = "https://$nsxManagerFqdn/api/v1/transport-node-profiles" - } else { + } + else { $uri = "https://$nsxManagerFqdn/policy/api/v1/infra/host-transport-node-profiles" } @@ -3393,7 +3910,8 @@ Function Remove-NonResponsiveHosts { $uri = "https://$nsxManagerFqdn/api/v1/transport-node-collections" LogMessage -type INFO -message "[$nsxManagerFqdn] Reattaching Transport Node Profile to Cluster $clusterName" $response = Invoke-WebRequest -Method POST -URI $uri -ContentType application/json -headers $headers -body $body - } else { + } + else { LogMessage -type ERROR -message "[$jumpboxName] Unable to determine NSX Manager Version. Check that it was successfully restored." Break } @@ -3464,10 +3982,12 @@ Function Add-HostsToCluster { if ($esxiConnection) { LogMessage -type INFO -message "[$newHost] Adding to cluster $clusterName" Add-VMHost $newHost -username root -password $esxiRootPassword -Location $clusterName -Force -Confirm:$false | Out-Null - } else { + } + else { Write-Error "[$newHost] Unable to connect. Host will not be added to the cluster" } - } else { + } + else { LogMessage -type INFO -message "[$newHost] Already part of $clusterName. Skipping" } } @@ -3621,193 +4141,187 @@ Function New-RebuiltVsanDatastore { $datastoreName = ($extractedSddcData.workloadDomains.vsphereClusterDetails | Where-Object { $_.name -eq $clusterName }).primaryDatastoreName $datastoreType = ($extractedSddcData.workloadDomains.vsphereClusterDetails | Where-Object { $_.name -eq $clusterName }).primaryDatastoreType - If ($datastoreType -in "VSAN", "VSAN_ESA", "VSAN_MAX") { - LogMessage -type INFO -message "[$jumpboxName] Connecting to Restored vCenter: $vCenterFQDN" - $restoredvCenterConnection = Connect-ViServer $vCenterFQDN -user $vCenterAdmin -password $vCenterAdminPassword - If ($datastoreType -eq "VSAN") { - $vmhosts = (Get-Cluster -name $clusterName | Get-VMHost | Sort-Object -property Name) - LogMessage -type INFO -message "[$($vmhosts[0].name)] Using host as reference for Eligible Physical Disks" - - $disks = ((Get-Cluster -name $clusterName | Get-VMHost | Sort-Object -property Name)[0] | Get-VMHostDisk) | Where-Object { $_.ScsiLun.VsanStatus -eq 'Eligible' } | Sort-Object -Property @{e = { $_.scsilun.runtimename } } - $disksDisplayObject = @() - $disksIndex = 1 - $disksDisplayObject += [pscustomobject]@{ - 'ID' = "ID" - 'canonicalName' = "Canonical Name" - 'size' = "Size (GB)" - 'ssd' = "SSD" - 'scsiLun' = "SCSI LUN ID" - } - $disksDisplayObject += [pscustomobject]@{ - 'ID' = "--" - 'canonicalName' = "--------------------" - 'size' = "-------------" - 'ssd' = "------" - 'scsiLun' = "-------------" - } - Foreach ($disk in $disks) { - If ($disk.ScsiLun.CapacityGB -ne $null) { - $disksDisplayObject += [pscustomobject]@{ - 'ID' = $disksIndex - 'canonicalName' = $disk.ScsiLun.CanonicalName - 'size' = $disk.ScsiLun.CapacityGB - 'ssd' = $disk.ScsiLun.IsSsd - 'scsiLun' = $disk.ScsiLun.RuntimeName - } - $disksIndex++ + LogMessage -type INFO -message "[$jumpboxName] Connecting to Restored vCenter: $vCenterFQDN" + $restoredvCenterConnection = Connect-ViServer $vCenterFQDN -user $vCenterAdmin -password $vCenterAdminPassword + If ($datastoreType -ne "VSAN_ESA") { + $vmhosts = (Get-Cluster -name $clusterName | Get-VMHost | Sort-Object -property Name) + LogMessage -type INFO -message "[$($vmhosts[0].name)] Using host as reference for Eligible Physical Disks" + + $disks = ((Get-Cluster -name $clusterName | Get-VMHost | Sort-Object -property Name)[0] | Get-VMHostDisk) | Where-Object { $_.ScsiLun.VsanStatus -eq 'Eligible' } | Sort-Object -Property @{e = { $_.scsilun.runtimename } } + $disksDisplayObject = @() + $disksIndex = 1 + $disksDisplayObject += [pscustomobject]@{ + 'ID' = "ID" + 'canonicalName' = "Canonical Name" + 'size' = "Size (GB)" + 'ssd' = "SSD" + 'scsiLun' = "SCSI LUN ID" + } + $disksDisplayObject += [pscustomobject]@{ + 'ID' = "--" + 'canonicalName' = "--------------------" + 'size' = "-------------" + 'ssd' = "------" + 'scsiLun' = "-------------" + } + Foreach ($disk in $disks) { + If ($disk.ScsiLun.CapacityGB -ne $null) { + $disksDisplayObject += [pscustomobject]@{ + 'ID' = $disksIndex + 'canonicalName' = $disk.ScsiLun.CanonicalName + 'size' = $disk.ScsiLun.CapacityGB + 'ssd' = $disk.ScsiLun.IsSsd + 'scsiLun' = $disk.ScsiLun.RuntimeName } + $disksIndex++ } + } - $diskGroupConfiguration = @() - $remainingDisksDisplayObject = $disksDisplayObject - Write-Host ""; $remainingDisksDisplayObject | format-table -Property @{Expression = " " }, id, canonicalName, size, ssd, scsiLun -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r", "`n") } + $diskGroupConfiguration = @() + $remainingDisksDisplayObject = $disksDisplayObject + Write-Host ""; $remainingDisksDisplayObject | format-table -Property @{Expression = " " }, id, canonicalName, size, ssd, scsiLun -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r", "`n") } + Do { + Write-Host ""; Write-Host " Enter the desired number of disk groups to create (between 1 and 5), or C to Cancel: " -ForegroundColor Yellow -nonewline + $diskGroupNumber = Read-Host + } Until (($diskGroupNumber -in "1", "2", "3", "4", "5") -or ($diskGroupNumber -eq "C")) + If ($diskGroupNumber -eq "C") { Break } + + #Loop Through Disk Group Creation + For ($i = 1; $i -le $diskGroupNumber; $i++) { + If ($i -gt 1) { + Write-Host ""; $remainingDisksDisplayObject | format-table -Property @{Expression = " " }, id, canonicalName, size, ssd -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r", "`n") } + } Do { - Write-Host ""; Write-Host " Enter the desired number of disk groups to create (between 1 and 5), or C to Cancel: " -ForegroundColor Yellow -nonewline - $diskGroupNumber = Read-Host - } Until (($diskGroupNumber -in "1", "2", "3", "4", "5") -or ($diskGroupNumber -eq "C")) - If ($diskGroupNumber -eq "C") { Break } - - #Loop Through Disk Group Creation - For ($i = 1; $i -le $diskGroupNumber; $i++) { - If ($i -gt 1) { - Write-Host ""; $remainingDisksDisplayObject | format-table -Property @{Expression = " " }, id, canonicalName, size, ssd -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r", "`n") } - } - Do { - If ($i -gt 1) { Write-Host "" }; Write-Host " Enter the ID of disk to use as Cache Disk for Disk Group $i, or C to Cancel: " -ForegroundColor Yellow -nonewline - $cacheDiskSelection = Read-Host - } Until (($cacheDiskSelection -in $remainingDisksDisplayObject.id) -OR ($cacheDiskSelection -eq "c")) - If ($cacheDiskSelection -eq "c") { Break } - $tempRemainingDisksDisplayObject = @() - Foreach ( $displayDisk in $remainingDisksDisplayObject) { - If ($displayDisk.id -ne $cacheDiskSelection) { - $tempRemainingDisksDisplayObject += $displayDisk - } + If ($i -gt 1) { Write-Host "" }; Write-Host " Enter the ID of disk to use as Cache Disk for Disk Group $i, or C to Cancel: " -ForegroundColor Yellow -nonewline + $cacheDiskSelection = Read-Host + } Until (($cacheDiskSelection -in $remainingDisksDisplayObject.id) -OR ($cacheDiskSelection -eq "c")) + If ($cacheDiskSelection -eq "c") { Break } + $tempRemainingDisksDisplayObject = @() + Foreach ( $displayDisk in $remainingDisksDisplayObject) { + If ($displayDisk.id -ne $cacheDiskSelection) { + $tempRemainingDisksDisplayObject += $displayDisk } - $remainingDisksDisplayObject = $tempRemainingDisksDisplayObject - Write-Host ""; $remainingDisksDisplayObject | format-table -Property @{Expression = " " }, id, canonicalName, size, ssd -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r", "`n") } - Do { - Write-Host ""; Write-Host " Enter a comma seperated list of IDs to be used as Capacity Disks for Disk Group $i, or C to Cancel: " -ForegroundColor Yellow -nonewline - $capacityDiskSelection = Read-Host - If ($capacityDiskSelection -ne "C") { - $capacityDiskSelectionInvalid = $false - $capacityDiskArray = $capacityDiskSelection -split (",") - Foreach ($capacityDisk in $capacityDiskArray) { - If ($capacityDisk -notin $disksDisplayObject.id) { - $capacityDiskSelectionInvalid = $true - } + } + $remainingDisksDisplayObject = $tempRemainingDisksDisplayObject + Write-Host ""; $remainingDisksDisplayObject | format-table -Property @{Expression = " " }, id, canonicalName, size, ssd -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r", "`n") } + Do { + Write-Host ""; Write-Host " Enter a comma seperated list of IDs to be used as Capacity Disks for Disk Group $i, or C to Cancel: " -ForegroundColor Yellow -nonewline + $capacityDiskSelection = Read-Host + If ($capacityDiskSelection -ne "C") { + $capacityDiskSelectionInvalid = $false + $capacityDiskArray = $capacityDiskSelection -split (",") + Foreach ($capacityDisk in $capacityDiskArray) { + If ($capacityDisk -notin $disksDisplayObject.id) { + $capacityDiskSelectionInvalid = $true } } - } Until (($capacityDiskSelectionInvalid -eq $false) -OR ($capacityDiskSelection -eq "c")) - If ($capacityDiskSelection -eq "c") { Break } - $diskGroupConfiguration += [PSCustomObject]@{ - 'cacheDiskID' = $cacheDiskSelection - 'capacityDiskIDs' = $capacityDiskArray } - $tempRemainingDisksDisplayObject = @() - Foreach ( $displayDisk in $remainingDisksDisplayObject) { - If ($displayDisk.id -notin $capacityDiskArray) { - $tempRemainingDisksDisplayObject += $displayDisk - } + } Until (($capacityDiskSelectionInvalid -eq $false) -OR ($capacityDiskSelection -eq "c")) + If ($capacityDiskSelection -eq "c") { Break } + $diskGroupConfiguration += [PSCustomObject]@{ + 'cacheDiskID' = $cacheDiskSelection + 'capacityDiskIDs' = $capacityDiskArray + } + $tempRemainingDisksDisplayObject = @() + Foreach ( $displayDisk in $remainingDisksDisplayObject) { + If ($displayDisk.id -notin $capacityDiskArray) { + $tempRemainingDisksDisplayObject += $displayDisk } - $remainingDisksDisplayObject = $tempRemainingDisksDisplayObject } - If (($cacheDiskSelection -eq "c") -or ($capacityDiskSelection -eq "c")) { Break } + $remainingDisksDisplayObject = $tempRemainingDisksDisplayObject + } + If (($cacheDiskSelection -eq "c") -or ($capacityDiskSelection -eq "c")) { Break } - $proposedConfigDisplayObject = @() - $configIndex = 1 + $proposedConfigDisplayObject = @() + $configIndex = 1 + $proposedConfigDisplayObject += [pscustomobject]@{ + 'diskGroup' = "Disk Group" + 'cacheDiskID' = "Cache Disk ID" + 'cacheDiskCN' = "Cache Disk Canonical Name" + 'cacheDiskCapacity' = "Cache Disk (GB)" + 'capacityDiskIDs' = "Capacity Disk IDs" + 'capacityCNs' = "Capacity Disk Canonical Names" + 'capacityDiskSize' = "Capacity Disks (GB)" + } + $proposedConfigDisplayObject += [pscustomobject]@{ + 'diskGroup' = "----------" + 'cacheDiskID' = "-------------" + 'cacheDiskCN' = "-------------------------" + 'cacheDiskCapacity' = "---------------" + 'capacityDiskIDs' = "-----------------" + 'capacityCNs' = "----------------------------------------" + 'capacityDiskSize' = "-------------------" + } + Foreach ($config in $diskGroupConfiguration) { $proposedConfigDisplayObject += [pscustomobject]@{ - 'diskGroup' = "Disk Group" - 'cacheDiskID' = "Cache Disk ID" - 'cacheDiskCN' = "Cache Disk Canonical Name" - 'cacheDiskCapacity' = "Cache Disk (GB)" - 'capacityDiskIDs' = "Capacity Disk IDs" - 'capacityCNs' = "Capacity Disk Canonical Names" - 'capacityDiskSize' = "Capacity Disks (GB)" + 'diskGroup' = $configIndex + 'cacheDiskID' = $config.cacheDiskID + 'cacheDiskCN' = ($disksDisplayObject | Where-Object { $_.id -eq $config.cacheDiskID }).canonicalName + 'cacheDiskCapacity' = ($disksDisplayObject | Where-Object { $_.id -eq $config.cacheDiskID }).size + 'capacityDiskIDs' = $config.capacityDiskIDs -join (", ") + 'capacityCNs' = (($disksDisplayObject | Where-Object { $_.id -in $config.capacityDiskIDs }).canonicalName) -join (", ") + 'capacityDiskSize' = (($disksDisplayObject | Where-Object { $_.id -in $config.capacityDiskIDs }).size) -join (", ") } - $proposedConfigDisplayObject += [pscustomobject]@{ - 'diskGroup' = "----------" - 'cacheDiskID' = "-------------" - 'cacheDiskCN' = "-------------------------" - 'cacheDiskCapacity' = "---------------" - 'capacityDiskIDs' = "-----------------" - 'capacityCNs' = "----------------------------------------" - 'capacityDiskSize' = "-------------------" - } - Foreach ($config in $diskGroupConfiguration) { - $proposedConfigDisplayObject += [pscustomobject]@{ - 'diskGroup' = $configIndex - 'cacheDiskID' = $config.cacheDiskID - 'cacheDiskCN' = ($disksDisplayObject | Where-Object { $_.id -eq $config.cacheDiskID }).canonicalName - 'cacheDiskCapacity' = ($disksDisplayObject | Where-Object { $_.id -eq $config.cacheDiskID }).size - 'capacityDiskIDs' = $config.capacityDiskIDs -join (", ") - 'capacityCNs' = (($disksDisplayObject | Where-Object { $_.id -in $config.capacityDiskIDs }).canonicalName) -join (", ") - 'capacityDiskSize' = (($disksDisplayObject | Where-Object { $_.id -in $config.capacityDiskIDs }).size) -join (", ") - } - $configIndex++ - } - Write-Host ""; Write-Host " Proposed Disk Group Configuration " -ForegroundColor Yellow - Write-Host ""; $proposedConfigDisplayObject | format-table -Property @{Expression = " " }, diskGroup, cacheDiskID, cacheDiskCN, cacheDiskCapacity, capacityDiskIDs, capacityCNs, capacityDiskSize -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r", "`n") } - Write-Host ""; Write-Host " Do you wish to proceed with the proposed configuration? (Y/N): " -ForegroundColor Yellow -nonewline - $proposedConfigAccepted = Read-Host - $proposedConfigAccepted = $proposedConfigAccepted -replace "`t|`n|`r", "" - If ($proposedConfigAccepted -eq "Y") { - LogMessage -type INFO -message "[$clusterName] Starting Parallel Disk Group Creation across all hosts" - Foreach ($vmHost in $vmHosts) { - $scriptBlock = { - $moduleFunctions = Import-Module VMware.CloudFoundation.InstanceRecovery -passthru - $restoredvCenterConnection = Connect-ViServer $using:vCenterFQDN -user $using:vCenterAdmin -password $using:vCenterAdminPassword - $vmhost = Get-VMHost -name $using:vmhost.name - $disks = Get-VMHost -name $using:vmhost.name | Get-VMHostDisk | Where-Object { $_.ScsiLun.VsanStatus -eq 'Eligible' } | Sort-Object -Property @{e = { $_.scsilun.runtimename } } - $disksDisplayObject = @() - $disksIndex = 1 - $disksDisplayObject += [pscustomobject]@{ - 'ID' = "ID" - 'canonicalName' = "Canonical Name" - 'size' = "Size (GB)" - 'ssd' = "SSD" - 'scsiLun' = "SCSI LUN ID" - } - $disksDisplayObject += [pscustomobject]@{ - 'ID' = "--" - 'canonicalName' = "--------------------" - 'size' = "-------------" - 'ssd' = "------" - 'scsiLun' = "-------------" - } - Foreach ($disk in $disks) { - If ($disk.ScsiLun.CapacityGB -ne $null) { - $disksDisplayObject += [pscustomobject]@{ - 'ID' = $disksIndex - 'canonicalName' = $disk.ScsiLun.CanonicalName - 'size' = $disk.ScsiLun.CapacityGB - 'ssd' = $disk.ScsiLun.IsSsd - 'scsiLun' = $disk.ScsiLun.RuntimeName - } - $disksIndex++ + $configIndex++ + } + Write-Host ""; Write-Host " Proposed Disk Group Configuration " -ForegroundColor Yellow + Write-Host ""; $proposedConfigDisplayObject | format-table -Property @{Expression = " " }, diskGroup, cacheDiskID, cacheDiskCN, cacheDiskCapacity, capacityDiskIDs, capacityCNs, capacityDiskSize -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r", "`n") } + Write-Host ""; Write-Host " Do you wish to proceed with the proposed configuration? (Y/N): " -ForegroundColor Yellow -nonewline + $proposedConfigAccepted = Read-Host + $proposedConfigAccepted = $proposedConfigAccepted -replace "`t|`n|`r", "" + If ($proposedConfigAccepted -eq "Y") { + LogMessage -type INFO -message "[$clusterName] Starting Parallel Disk Group Creation across all hosts" + Foreach ($vmHost in $vmHosts) { + $scriptBlock = { + $moduleFunctions = Import-Module VMware.CloudFoundation.InstanceRecovery -passthru + $restoredvCenterConnection = Connect-ViServer $using:vCenterFQDN -user $using:vCenterAdmin -password $using:vCenterAdminPassword + $vmhost = Get-VMHost -name $using:vmhost.name + $disks = Get-VMHost -name $using:vmhost.name | Get-VMHostDisk | Where-Object { $_.ScsiLun.VsanStatus -eq 'Eligible' } | Sort-Object -Property @{e = { $_.scsilun.runtimename } } + $disksDisplayObject = @() + $disksIndex = 1 + $disksDisplayObject += [pscustomobject]@{ + 'ID' = "ID" + 'canonicalName' = "Canonical Name" + 'size' = "Size (GB)" + 'ssd' = "SSD" + 'scsiLun' = "SCSI LUN ID" + } + $disksDisplayObject += [pscustomobject]@{ + 'ID' = "--" + 'canonicalName' = "--------------------" + 'size' = "-------------" + 'ssd' = "------" + 'scsiLun' = "-------------" + } + Foreach ($disk in $disks) { + If ($disk.ScsiLun.CapacityGB -ne $null) { + $disksDisplayObject += [pscustomobject]@{ + 'ID' = $disksIndex + 'canonicalName' = $disk.ScsiLun.CanonicalName + 'size' = $disk.ScsiLun.CapacityGB + 'ssd' = $disk.ScsiLun.IsSsd + 'scsiLun' = $disk.ScsiLun.RuntimeName } + $disksIndex++ } - For ($i = 1; $i -le $using:diskGroupNumber; $i++) { - $diskGroupConfigurationIndex = ($i - 1) - $diskGroupConfiguration = $using:diskGroupConfiguration - $cacheDiskCanonicalName = (($disksDisplayObject | Where-Object { $_.id -eq $diskGroupConfiguration[$diskGroupConfigurationIndex].cacheDiskID }).canonicalName) - $capacityDiskCanonicalNames = (($disksDisplayObject | Where-Object { $_.id -in $diskGroupConfiguration[$diskGroupConfigurationIndex].capacityDiskIDs }).canonicalName) - & $moduleFunctions { LogMessage -type INFO -message "[$($vmhost.name)] Creating VSAN Disk Group $i" } - New-VsanDiskGroup -VMHost $vmhost -SsdCanonicalName $cacheDiskCanonicalName -DataDiskCanonicalName $capacityDiskCanonicalNames | Out-Null - } - Disconnect-VIServer -Server $global:DefaultVIServers -Force -Confirm:$false } - Start-Job -scriptblock $scriptBlock -ArgumentList ($diskGroupNumber, $diskGroupConfiguration, $vmhost, $vCenterFQDN, $vCenterAdmin, $vCenterAdminPassword) | Out-Null + For ($i = 1; $i -le $using:diskGroupNumber; $i++) { + $diskGroupConfigurationIndex = ($i - 1) + $diskGroupConfiguration = $using:diskGroupConfiguration + $cacheDiskCanonicalName = (($disksDisplayObject | Where-Object { $_.id -eq $diskGroupConfiguration[$diskGroupConfigurationIndex].cacheDiskID }).canonicalName) + $capacityDiskCanonicalNames = (($disksDisplayObject | Where-Object { $_.id -in $diskGroupConfiguration[$diskGroupConfigurationIndex].capacityDiskIDs }).canonicalName) + & $moduleFunctions { LogMessage -type INFO -message "[$($vmhost.name)] Creating VSAN Disk Group $i" } + New-VsanDiskGroup -VMHost $vmhost -SsdCanonicalName $cacheDiskCanonicalName -DataDiskCanonicalName $capacityDiskCanonicalNames | Out-Null + } + Disconnect-VIServer -Server $global:DefaultVIServers -Force -Confirm:$false } - Get-Job | Receive-Job -Wait -AutoRemoveJob + Start-Job -scriptblock $scriptBlock -ArgumentList ($diskGroupNumber, $diskGroupConfiguration, $vmhost, $vCenterFQDN, $vCenterAdmin, $vCenterAdminPassword) | Out-Null } + Get-Job | Receive-Job -Wait -AutoRemoveJob } - If ($datastoreType -in "VSAN", "VSAN_ESA", "VSAN_MAX") { - LogMessage -type INFO -message "[$clusterName] Renaming new datastore to original name: $datastoreName" - Get-Cluster -name $clusterName | Get-Datastore -Name "vsanDatastore*" | Set-Datastore -Name $datastoreName | Out-Null - } - } else { - LogMessage -type NOTE -message "[$clusterName] No hosted vSAN datastores were found to rebuild" } + LogMessage -type INFO -message "[$clusterName] Renaming new datastore to original name: $datastoreName" + Get-Cluster -name $clusterName | Get-Datastore -Name "vsanDatastore*" | Set-Datastore -Name $datastoreName | Out-Null LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)" } Export-ModuleMember -Function New-RebuiltVsanDatastore @@ -3858,7 +4372,8 @@ Function New-RebuiltVdsConfiguration { $cluster = ($workloadDomain.vsphereClusterDetails | Where-Object { $_.name -eq $clustername }) If (($workloadDomain.domainType -eq "MANAGEMENT") -and ($isPrimaryCluster -eq 't')) { $isPrimaryManagementCluster = $true - } else { + } + else { $isPrimaryManagementCluster = $false } @@ -3908,7 +4423,8 @@ Function New-RebuiltVdsConfiguration { Write-Host ""; $remainingNicsDisplayObject | format-table -Property @{Expression = " " }, id, deviceName, driver, linkStatus, description -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r", "`n") } If ($cluster.vdsDetails[$vdsConfigurationIndex].transportZones) { $networksDisplay = ($cluster.vdsDetails[$vdsConfigurationIndex].networks += "OVERLAY") -join (",") - } else { + } + else { $networksDisplay = $cluster.vdsDetails[$vdsConfigurationIndex].networks -join (",") } Write-Host ""; Write-Host " Recreating " -ForegroundColor Yellow -nonewline; Write-Host "$($cluster.vdsDetails[$vdsConfigurationIndex].dvsName)" -ForegroundColor cyan -nonewline; Write-Host " which contained the networks: " -ForegroundColor Yellow -nonewline; Write-Host "$networksDisplay" -ForegroundColor Cyan @@ -3978,7 +4494,8 @@ Function New-RebuiltVdsConfiguration { If (($vds.portgroups | Where-Object { $_.transportType -eq 'VM_MANAGEMENT' }).name) { $managementVmPortGroupName = ($vds.portgroups | Where-Object { $_.transportType -eq 'VM_MANAGEMENT' }).name - } else { + } + else { $managementVmPortGroupName = ($vds.portgroups | Where-Object { $_.transportType -eq 'MANAGEMENT' }).name } $managementPortGroupName = ($vds.portgroups | Where-Object { $_.transportType -eq 'MANAGEMENT' }).name @@ -4007,7 +4524,8 @@ Function New-RebuiltVdsConfiguration { If ($hostMoRef -notin $vdsHosts) { LogMessage -type INFO -message "[$($vmhost.name)] Adding to $($vds.vdsName)" Get-VDSwitch -name $vds.vdsName | Add-VDSwitchVMHost -vmhost $vmHost -confirm:$false - } else { + } + else { LogMessage -type INFO -message "[$($vmhost.name)] Already in $($vds.vdsName). Skipping" } @@ -4016,10 +4534,12 @@ Function New-RebuiltVdsConfiguration { If ($portgroupArray.count -ne 0) { LogMessage -type INFO -message "[$($vmhost.name)] Adding Physical Adapter $($vds.nicNames[0]) to $($vds.vdsName) and migrating $($vmNicArray.name -join(", "))" Get-VDSwitch -name $vds.vdsName | Add-VDSwitchPhysicalNetworkAdapter -VMHostPhysicalNic $vmnicMinusOne -VMHostVirtualNic $vmNicArray -VirtualNicPortgroup $portgroupArray -confirm:$false - } else { + } + else { Get-VDSwitch -name $vds.vdsName | Add-VDSwitchPhysicalNetworkAdapter -VMHostPhysicalNic $vmnicMinusOne -confirm:$false } - } else { + } + else { LogMessage -type INFO -message "[$($vmhost.name)] Physical Adapter $($vds.nicNames[0]) already in $($vds.vdsName). Skipping" } @@ -4032,7 +4552,8 @@ Function New-RebuiltVdsConfiguration { If ((Get-VM -Name $vmToMove | Get-NetworkAdapter).NetworkName -ne $managementVmPortGroupName) { LogMessage -type INFO -message "[$($vmToMove.name)] Moving to $managementVmPortGroupName" Get-VM -Name $vmToMove | Get-NetworkAdapter | Set-NetworkAdapter -NetworkName $managementVmPortGroupName -confirm:$false | Out-Null - } else { + } + else { LogMessage -type INFO -message "[$($vmToMove.name)] Already moved to $managementVmPortGroupName. Skipping" } } @@ -4047,7 +4568,8 @@ Function New-RebuiltVdsConfiguration { If ($hostvss) { LogMessage -type INFO -message "[$($vmhost.name)] Removing vSwitch0" Get-VMHost -Name $vmhost | Get-VirtualSwitch -Name "vSwitch0" | Remove-VirtualSwitch -Confirm:$false | Out-Null - } else { + } + else { LogMessage -type INFO -message "[$($vmhost.name)] vSwitch0 already removed. Skipping" } @@ -4063,7 +4585,8 @@ Function New-RebuiltVdsConfiguration { LogMessage -type INFO -message "[$($vmhost.name)] Adding Additional Nic $nic to $($vds.vdsName)" $additionalNic = $vmhost | Get-VMHostNetworkAdapter -Physical -Name $nic Get-VDSwitch -name $vds.vdsName | Add-VDSwitchPhysicalNetworkAdapter -VMHostPhysicalNic $additionalNic -confirm:$false - } else { + } + else { LogMessage -type INFO -message "[$($vmhost.name)] Physical Adapter $nic already in $($vds.vdsName). Skipping" } } @@ -4170,7 +4693,8 @@ Function Backup-ClusterVMLocations { $allVMs += $vmSettings } $allVMs | ConvertTo-Json -depth 10 | Out-File "$clusterName-vmLocations.json" - } Catch { + } + Catch { catchWriter -object $_ } LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)" @@ -4266,7 +4790,8 @@ Function Backup-ClusterDRSGroupsAndRules { } $drsBackup | ConvertTo-Json -depth 10 | Out-File "$clusterName-drsConfiguration.json" - } Catch { + } + Catch { catchWriter -object $_ } LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)" @@ -4308,7 +4833,8 @@ Function Backup-ClusterVMTags { $allVMs += $vmSettings } $allVMs | ConvertTo-Json -depth 10 | Out-File "$clusterName-vmTags.json" - } Catch { + } + Catch { catchWriter -object $_ } LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)" @@ -4440,10 +4966,12 @@ Function Restore-ClusterVMOverrides { $cluster.ExtensionData.ReconfigureComputeResource($spec, $True) } } - } else { + } + else { Write-Error "$jsonfile not found" } - } catch { + } + catch { catchWriter -object $_ } LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)" @@ -4489,16 +5017,19 @@ Function Restore-ClusterVMLocations { LogMessage -type INFO -message "[$($vmLocation.name)] Setting ResourcePool to $($vmLocation.resourcePool)" Move-VM -VM $vm -Destination $vmLocation.resourcePool -confirm:$false } - } else { + } + else { Write-Error "[$(Get-VM -name $vmLocation.name)] Not found. Check that it has been restored" } } } - } else { + } + else { $jumpboxName = hostname Write-Error "[$jumpboxName] $jsonfile not found" } - } catch { + } + catch { catchWriter -object $_ } LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)" @@ -4540,17 +5071,20 @@ Function Restore-ClusterDRSGroupsAndRules { LogMessage -type INFO -message "[$member] Adding to VMHostGroup $($vmDrsGroup.name)" Set-DrsClusterGroup -DrsClusterGroup $vmDrsGroup.name -Add -VMHost $member -confirm:$false | Out-Null } - } elseif ($vmDrsGroup.type -eq "VMGroup") { + } + elseif ($vmDrsGroup.type -eq "VMGroup") { Foreach ($member in $vmDrsGroup.members) { LogMessage -type INFO -message "[$member] Adding to VMGroup $($vmDrsGroup.name)" Set-DrsClusterGroup -DrsClusterGroup $vmDrsGroup.name -Add -VM $member -confirm:$false | Out-Null } } - } else { + } + else { If ($vmDrsGroup.type -eq "VMHostGroup") { LogMessage -type INFO -message "[$($vmDrsGroup.name)] Creating VMHostGroup with Members $($vmDrsGroup.members)" New-DrsClusterGroup -Name $vmDrsGroup.name -VMHost $vmDrsGroup.members -Cluster $clusterName | Out-Null - } elseif ($vmDrsGroup.type -eq "VMGroup") { + } + elseif ($vmDrsGroup.type -eq "VMGroup") { LogMessage -type INFO -message "[$($vmDrsGroup.name)] Creating VMGroup with Members $($vmDrsGroup.members)" New-DrsClusterGroup -Name $vmDrsGroup.name -VM $vmDrsGroup.members -Cluster $clusterName | Out-Null } @@ -4562,7 +5096,8 @@ Function Restore-ClusterDRSGroupsAndRules { If ($vmRule) { LogMessage -type INFO -message "[$($vmAffinityRule.name)] Setting VM Rule with Members $($vmAffinityRule.members)" Set-DrsRule -rule $vmRule -VM $vmAffinityRule.members -Enabled $true -confirm:$false | Out-Null - } else { + } + else { LogMessage -type INFO -message "[$($vmAffinityRule.name)] Creating VM Rule with Members $($vmAffinityRule.members)" New-DrsRule -cluster $clusterName -name $vmAffinityRule.name -VM $vmAffinityRule.members -keepTogether $vmAffinityRule.keepTogether -Enabled $true | Out-Null } @@ -4573,7 +5108,8 @@ Function Restore-ClusterDRSGroupsAndRules { If ($hostRule) { LogMessage -type INFO -message "[$($vmHostAffinityRule.name)] Setting VMHost Rule with VM Group $($vmHostAffinityRule.vmGroupName) and Host Group $($vmHostAffinityRule.hostGroupName)" Set-DrsVMHostRule -rule $hostRule -VMGroup $vmHostAffinityRule.vmGroupName -VMHostGroup $vmHostAffinityRule.hostGroupName -Type $vmHostAffinityRule.variant -confirm:$false | Out-Null - } else { + } + else { LogMessage -type INFO -message "[$($vmHostAffinityRule.name)] Creating VMHost Rule with VM Group $($vmHostAffinityRule.vmGroupName) and Host Group $($vmHostAffinityRule.hostGroupName)" New-DrsVMHostRule -Name $vmHostAffinityRule.name -Cluster $clusterName -VMGroup $vmHostAffinityRule.vmGroupName -VMHostGroup $vmHostAffinityRule.hostGroupName -Type $vmHostAffinityRule.variant | Out-Null } @@ -4597,11 +5133,13 @@ Function Restore-ClusterDRSGroupsAndRules { $cluster.ExtensionData.ReconfigureComputeResource($spec, $True) } } - } else { + } + else { $jumpboxName = hostname Write-Error "[$jumpboxName] $jsonfile not found" } - } catch { + } + catch { catchWriter -object $_ } LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)" @@ -4641,16 +5179,19 @@ Function Restore-ClusterVMTags { If ($vm) { LogMessage -type INFO -message "[$($vmTag.Entity)] Setting VM Tag to $($vmTag.Tag)" New-TagAssignment -Entity $vm -Tag $vmTag.Tag -confirm:$false | Out-Null - } else { + } + else { Write-Error "[$(Get-VM -name $vmTag.Entity)] Not found. Check that it has been restored" } } } - } else { + } + else { $jumpboxName = hostname Write-Error "[$jumpboxName] $jsonfile not found" } - } catch { + } + catch { catchWriter -object $_ } LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)" @@ -4734,8 +5275,8 @@ Function Invoke-NSXManagerRestore { $nsxManagerFQDN = $selectedNsxManager.hostname $nsxManagerIP = $selectedNsxManager.ip - $nsxManagerAdminUsername = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.credentialType -eq "API") }).username - $nsxManagerAdminPassword = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.credentialType -eq "API") }).password + $nsxManagerAdminUsername = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.username -eq "admin") }).username + $nsxManagerAdminPassword = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.username -eq "admin") }).password #Retrieve Key of SFTP Server LogMessage -type INFO -message "[$jumpboxName] Retrieving SSH Fingerprint of $sftpServer" @@ -4751,7 +5292,8 @@ Function Invoke-NSXManagerRestore { Do { Try { $existingBackup = (Invoke-WebRequest -Method GET -URI $uri -ContentType application/json -headers $headers).content | ConvertFrom-Json - } catch { + } + catch { Sleep 30 } } Until ($existingBackup) @@ -4855,10 +5397,12 @@ Function Invoke-NSXManagerRestore { }" $resumeUri = "https://$nsxManagerFQDN/api/v1/cluster/restore?action=advance" $resumeRestore = (Invoke-WebRequest -Method POST -URI $resumeUri -ContentType application/json -body $body -headers $headers).content | ConvertFrom-Json - } else { + } + else { LogMessage -type INFO -message "[$nsxManagerFQDN] Restore is currently $($restoreStatus.status.value)" } - } Catch {} + } + Catch {} } Until ($restoreStatus.status.value -eq "SUCCESS") LogMessage -type INFO -message "[$nsxManagerFQDN] Restore finished with status: $($restoreStatus.status.value)" LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)" @@ -4956,7 +5500,8 @@ Function Invoke-NSXEdgeClusterRecovery { If ($allEdgeTransportNodes) { LogMessage -type INFO -message "[$nsxManagerFqdn] Found Edges to recover: $($allEdgeTransportNodes.display_name -join(","))" - } else { + } + else { LogMessage -type INFO -message "[$nsxManagerFqdn] No Edges found needing recovery" } #Redeploy Failed Edges @@ -4986,7 +5531,8 @@ Function Invoke-NSXEdgeClusterRecovery { If ($edgeLocation.type -eq "ResourcePool") { New-VM -VMhost (get-cluster -name $clusterName | Get-VMHost | Get-Random ) -Name $edge.display_name -Datastore $datastore -resourcePool $edgeLocation.name -DiskGB 200 -DiskStorageFormat Thin -MemoryGB $MemoryGB -NumCpu $NumCpu -portgroup $portgroup -GuestID "ubuntu64Guest" -Confirm:$false | Out-Null - } else { + } + else { New-VM -VMhost (get-cluster -name $clusterName | Get-VMHost | Get-Random ) -Name $edge.display_name -Datastore $datastore -DiskGB 200 -DiskStorageFormat Thin -MemoryGB $MemoryGB -NumCpu $NumCpu -portgroup $portgroup -GuestID "ubuntu64Guest" -Confirm:$false | Out-Null } do { @@ -5027,7 +5573,8 @@ Function Invoke-NSXEdgeClusterRecovery { $edgeResponse = (Invoke-WebRequest -Method GET -URI $uri -ContentType application/json -headers $headers).content $uri = "https://$nsxManagerFqdn/api/v1/transport-nodes/$($edge.node_id)?action=redeploy" $edgeRedeploy = Invoke-WebRequest -Method POST -URI $uri -ContentType application/json -body $edgeResponse -headers $headers - } else { + } + else { LogMessage -type INFO -message "[$($edge.display_name)] Not in a suitable state for redeployment. Please review and retry" } } @@ -5066,10 +5613,10 @@ Function Add-AdditionalNSXManagers { LogMessage -type INFO -message "[$jumpboxName] Reading Extracted Data" $extractedDataFilePath = (Resolve-Path -Path $extractedSDDCDataFile).path $extractedSddcData = Get-Content $extractedDataFilePath | ConvertFrom-JSON - $workloadDomainDetails = ($extractedSDDCData.workloadDomains | Where-Object {$_.domainName -eq $workloadDomain}) + $workloadDomainDetails = ($extractedSDDCData.workloadDomains | Where-Object { $_.domainName -eq $workloadDomain }) $nsxNodes = $workloadDomainDetails.nsxNodeDetails - $nsxManagersDisplayObject=@() + $nsxManagersDisplayObject = @() $nsxManagersIndex = 1 $nsxManagersDisplayObject += [pscustomobject]@{ 'ID' = "ID" @@ -5086,39 +5633,39 @@ Function Add-AdditionalNSXManagers { } $nsxManagersIndex++ } - Write-Host ""; $nsxManagersDisplayObject | format-table -Property @{Expression =" "},id,Manager -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r","`n") } + Write-Host ""; $nsxManagersDisplayObject | format-table -Property @{Expression = " " }, id, Manager -autosize -HideTableHeaders | Out-String | ForEach-Object { $_.Trim("`r", "`n") } Do { Write-Host ""; Write-Host " Enter the ID of the First NSX Manager (i.e. the one you peformed the restore on), or C to Cancel: " -ForegroundColor Yellow -nonewline $nsxManagerSelection = Read-Host } Until (($nsxManagerSelection -in $nsxManagersDisplayObject.ID) -OR ($nsxManagerSelection -eq "c")) - If ($nsxManagerSelection -eq "c") {Break} - $selectedNsxManager = $nsxNodes | Where-Object {$_.vmName -eq ($nsxManagersDisplayObject | Where-Object {$_.id -eq $nsxManagerSelection}).manager } - $otherNsxManagers = $nsxNodes | Where-Object {$_.vmName -ne ($nsxManagersDisplayObject | Where-Object {$_.id -eq $nsxManagerSelection}).manager } + If ($nsxManagerSelection -eq "c") { Break } + $selectedNsxManager = $nsxNodes | Where-Object { $_.vmName -eq ($nsxManagersDisplayObject | Where-Object { $_.id -eq $nsxManagerSelection }).manager } + $otherNsxManagers = $nsxNodes | Where-Object { $_.vmName -ne ($nsxManagersDisplayObject | Where-Object { $_.id -eq $nsxManagerSelection }).manager } $nsxManagerFQDN = $selectedNsxManager.hostname - $nsxManagerAdminUsername = ($extractedSddcData.passwords | Where-Object {($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.credentialType -eq "API")}).username - $nsxManagerAdminPassword = ($extractedSddcData.passwords | Where-Object {($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.credentialType -eq "API")}).password + $nsxManagerAdminUsername = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.credentialType -eq "API") }).username + $nsxManagerAdminPassword = ($extractedSddcData.passwords | Where-Object { ($_.entityType -eq "NSXT_MANAGER") -and ($_.domainName -eq $workloadDomain) -and ($_.credentialType -eq "API") }).password #Create Headers $headers = VCFIRCreateHeader -username $nsxManagerAdminUsername -password $nsxManagerAdminPassword #Check for Compatible NSX Manager version $uri = "https://$nsxManagerFqdn/api/v1/node" - $nsxManagerVersion = [INT](((((Invoke-WebRequest -Method GET -URI $uri -ContentType application/json -headers $headers).content | ConvertFrom-Json).product_version).replace(".","")).substring(0,3)) + $nsxManagerVersion = [INT](((((Invoke-WebRequest -Method GET -URI $uri -ContentType application/json -headers $headers).content | ConvertFrom-Json).product_version).replace(".", "")).substring(0, 3)) If ($nsxManagerVersion) { #Get NSX Nodes LogMessage -type INFO -message "[$nsxManagerFQDN] Getting Cluster Node Details" $uri = "https://$nsxManagerFQDN/api/v1/cluster/" $clusterNodes = ((Invoke-WebRequest -Method GET -URI $uri -ContentType application/json -headers $headers).content | ConvertFrom-Json).nodes - $otherclusterNodeIDs = ($clusterNodes | Where-Object {$_.fqdn -in $otherNsxManagers.hostname}).node_uuid #Potentially only required in NSX 3 + $otherclusterNodeIDs = ($clusterNodes | Where-Object { $_.fqdn -in $otherNsxManagers.hostname }).node_uuid #Potentially only required in NSX 3 #Get Certificates LogMessage -type INFO -message "[$nsxManagerFQDN] Getting Cluster Node Certificate Details" $uri = "https://$nsxManagerFQDN/api/v1/trust-management/certificates" $allcertificates = (Invoke-WebRequest -Method GET -URI $uri -ContentType application/json -headers $headers).content | ConvertFrom-Json - $signedCertificates = $allcertificates.results | Where-Object {$_.resource_type -eq "certificate_signed"} + $signedCertificates = $allcertificates.results | Where-Object { $_.resource_type -eq "certificate_signed" } LogMessage -type INFO -message "[$nsxManagerFQDN] Starting SSH" $uri = "https://$nsxManagerFqdn/api/v1/node/services/ssh?action=start" @@ -5154,7 +5701,7 @@ Function Add-AdditionalNSXManagers { #$unwantedOutput = $stream.Readline() #$unwantedOutput = $stream.Readline() $clusterIdOutput = $stream.Read() - $clusterId = (($clusterIdOutput.split("Cluster Id: "))[1]).Substring(0,36) + $clusterId = (($clusterIdOutput.split("Cluster Id: "))[1]).Substring(0, 36) LogMessage -type INFO -message "[$nsxManagerFQDN] Cluster ID: $clusterId retrieved" LogMessage -type INFO -message "[$nsxManagerFQDN] Getting Certificate API Thumbprint" @@ -5252,7 +5799,8 @@ Function Add-AdditionalNSXManagers { } #> } - } else { + } + else { LogMessage -type ERROR -message "[$jumpboxName] Unable to determine NSX Manager Version. Check that it was successfully restored." } LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)" @@ -5311,7 +5859,7 @@ Function Resolve-PhysicalHostTransportNodes { #Check for Compatible NSX Manager version $uri = "https://$nsxManagerFqdn/api/v1/node" - $nsxManagerVersion = [INT](((((Invoke-WebRequest -Method GET -URI $uri -ContentType application/json -headers $headers).content | ConvertFrom-Json).product_version).replace(".","")).substring(0,3)) + $nsxManagerVersion = [INT](((((Invoke-WebRequest -Method GET -URI $uri -ContentType application/json -headers $headers).content | ConvertFrom-Json).product_version).replace(".", "")).substring(0, 3)) If ($nsxManagerVersion) { If ($nsxManagerVersion -lt "412") { @@ -5345,11 +5893,13 @@ Function Resolve-PhysicalHostTransportNodes { #LogMessage -type INFO -message "[$nsxManagerFqdn] Resolving NSX Installation on $hostID" $response = Invoke-WebRequest -Method POST -URI $uri -ContentType application/json -headers $headers -body $body } - } else { + } + else { LogMessage -type NOTE -message "[$jumpboxName] This cmdlet is not required with NSX Manager version 4.1.2 and later" } - } else { + } + else { LogMessage -type ERROR -message "[$jumpboxName] Unable to determine NSX Manager Version. Check that it was successfully restored." } LogMessage -type NOTE -message "[$jumpboxName] Completed Task $($MyInvocation.MyCommand)"