diff --git a/Modules/Backup-VCSA.psm1 b/Modules/Backup-VCSA.psm1 deleted file mode 100644 index 271a7ff..0000000 --- a/Modules/Backup-VCSA.psm1 +++ /dev/null @@ -1,202 +0,0 @@ -Function Backup-VCSAToFile { -<# - .NOTES - =========================================================================== - Created by: Brian Graf - Date: October 30, 2016 - Organization: VMware - Blog: www.vtagion.com - Twitter: @vBrianGraf - =========================================================================== - - .SYNOPSIS - This function will allow you to create a full or partial backup of your - VCSA appliance. (vSphere 6.5 and higher) - - .DESCRIPTION - Use this function to backup your VCSA to a remote location - - .EXAMPLE - [VMware.VimAutomation.Cis.Core.Types.V1.Secret]$BackupPassword = "VMw@re123" - $Comment = "First API Backup" - $LocationType = "FTP" - $location = "10.144.99.5/vcsabackup-$((Get-Date).ToString('yyyy-MM-dd-hh-mm'))" - $LocationUser = "admin" - [VMware.VimAutomation.Cis.Core.Types.V1.Secret]$locationPassword = "VMw@re123" - PS C:\> Backup-VCSAToFile -BackupPassword $BackupPassword -LocationType $LocationType -Location $location -LocationUser $LocationUser -LocationPassword $locationPassword -Comment "This is a demo" -ShowProgress -FullBackup - - - .NOTES - Credit goes to @AlanRenouf for sharing the base of this function with me which I was able to take and make more robust as well as add in progress indicators - You must be connected to the CisService for this to work, if you are not connected, the function will prompt you for your credentials - If a -LocationType is not chosen, the function will default to FTP. - The destination location for a backup must be an empty folder (easiest to use the get-date cmdlet in the location) - -ShowProgress will give you a progressbar as well as updates in the console - -SeatBackup will only backup the config whereas -Fullbackup grabs the historical data as well -#> - param ( - [Parameter(ParameterSetName=’FullBackup’)] - [switch]$FullBackup, - [Parameter(ParameterSetName=’SeatBackup’)] - [switch]$SeatBackup, - [ValidateSet('FTPS', 'HTTP', 'SCP', 'HTTPS', 'FTP')] - $LocationType = "FTP", - $Location, - $LocationUser, - [VMware.VimAutomation.Cis.Core.Types.V1.Secret]$LocationPassword, - [VMware.VimAutomation.Cis.Core.Types.V1.Secret]$BackupPassword, - $Comment = "Backup job", - [switch]$ShowProgress - ) - Begin { - if (!($global:DefaultCisServers)){ - [System.Windows.Forms.MessageBox]::Show("It appears you have not created a connection to the CisServer. You will now be prompted to enter your vCenter credentials to continue" , "Connect to CisServer") | out-null - $Connection = Connect-CisServer $global:DefaultVIServer - } else { - $Connection = $global:DefaultCisServers - } - if ($FullBackup) {$parts = @("common","seat")} - if ($SeatBackup) {$parts = @("seat")} - } - Process{ - $BackupAPI = Get-CisService com.vmware.appliance.recovery.backup.job - $CreateSpec = $BackupAPI.Help.create.piece.CreateExample() - $CreateSpec.parts = $parts - $CreateSpec.backup_password = $BackupPassword - $CreateSpec.location_type = $LocationType - $CreateSpec.location = $Location - $CreateSpec.location_user = $LocationUser - $CreateSpec.location_password = $LocationPassword - $CreateSpec.comment = $Comment - try { - $BackupJob = $BackupAPI.create($CreateSpec) - } - catch { - Write-Error $Error[0].exception.Message - } - - - If ($ShowProgress){ - do { - $BackupAPI.get("$($BackupJob.ID)") | select id, progress, state - $progress = ($BackupAPI.get("$($BackupJob.ID)").progress) - Write-Progress -Activity "Backing up VCSA" -Status $BackupAPI.get("$($BackupJob.ID)").state -PercentComplete ($BackupAPI.get("$($BackupJob.ID)").progress) -CurrentOperation "$progress% Complete" - start-sleep -seconds 5 - } until ($BackupAPI.get("$($BackupJob.ID)").progress -eq 100 -or $BackupAPI.get("$($BackupJob.ID)").state -ne "INPROGRESS") - - $BackupAPI.get("$($BackupJob.ID)") | select id, progress, state - } - Else { - $BackupJob | select id, progress, state - } - } - End {} -} - -Function Get-VCSABackupJobs { -<# - .NOTES - =========================================================================== - Created by: Brian Graf - Date: October 30, 2016 - Organization: VMware - Blog: www.vtagion.com - Twitter: @vBrianGraf - =========================================================================== - - .SYNOPSIS - Get-VCSABackupJobs returns a list of all backup jobs VCSA has ever performed (vSphere 6.5 and higher) - - .DESCRIPTION - Get-VCSABackupJobs returns a list of all backup jobs VCSA has ever performed - - .EXAMPLE - PS C:\> Get-VCSABackupJobs - - .NOTES - The values returned are read as follows: - YYYYMMDD-hhmmss-vcsabuildnumber - You can pipe the results of this function into the Get-VCSABackupStatus function - Get-VCSABackupJobs | select -First 1 | Get-VCSABackupStatus <- Most recent backup -#> - param ( - [switch]$ShowNewest - ) - Begin { - if (!($global:DefaultCisServers)){ - [System.Windows.Forms.MessageBox]::Show("It appears you have not created a connection to the CisServer. You will now be prompted to enter your vCenter credentials to continue" , "Connect to CisServer") | out-null - $Connection = Connect-CisServer $global:DefaultVIServer - } else { - $Connection = $global:DefaultCisServers - } - } - Process{ - - $BackupAPI = Get-CisService com.vmware.appliance.recovery.backup.job - - try { - if ($ShowNewest) { - $results = $BackupAPI.list() - $results[0] - } else { - $BackupAPI.list() - } - } - catch { - Write-Error $Error[0].exception.Message - } - - } - - End {} -} - -Function Get-VCSABackupStatus { -<# - .NOTES - =========================================================================== - Created by: Brian Graf - Date: October 30, 2016 - Organization: VMware - Blog: www.vtagion.com - Twitter: @vBrianGraf - =========================================================================== - - .SYNOPSIS - Returns the ID, Progress, and State of a VCSA backup (vSphere 6.5 and higher) - - .DESCRIPTION - Returns the ID, Progress, and State of a VCSA backup - - .EXAMPLE - PS C:\> $backups = Get-VCSABackupJobs - $backups[0] | Get-VCSABackupStatus - - .NOTES - The BackupID can be piped in from the Get-VCSABackupJobs function and can return multiple job statuses -#> - Param ( - [parameter(ValueFromPipeline=$True)] - [string[]]$BackupID - ) - Begin { - if (!($global:DefaultCisServers)){ - [System.Windows.Forms.MessageBox]::Show("It appears you have not created a connection to the CisServer. You will now be prompted to enter your vCenter credentials to continue" , "Connect to CisServer") | out-null - $Connection = Connect-CisServer $global:DefaultVIServer - } else { - $Connection = $global:DefaultCisServers - } - - $BackupAPI = Get-CisService com.vmware.appliance.recovery.backup.job - } - Process{ - - foreach ($id in $BackupID) { - $BackupAPI.get("$id") | select id, progress, state - } - - - } - - End {} -} diff --git a/Modules/Get-NICDetails.psm1 b/Modules/Get-NICDetails.psm1 deleted file mode 100644 index 30f1440..0000000 --- a/Modules/Get-NICDetails.psm1 +++ /dev/null @@ -1,93 +0,0 @@ -function Get-NICDetails { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2017.02 ver 1.0 Base Release - =========================================================================== - External Code Sources: - - - =========================================================================== - Tested Against Environment: - vSphere Version: ESXi 6.0 U2, ESXi 6.5 - PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1 - PowerShell Version: 4.0, 5.0 - OS Version: Windows 8.1, Server 2008 R2, Server 2012 R2 - Keyword: ESXi, NIC, vmnic, Driver, Firmware - =========================================================================== - - .DESCRIPTION - Reports Firmware and Driver Details for your ESXi vmnics. - - .Example - Get-NICDetails -Clustername * - - .PARAMETER Clustername - Name or Wildcard of your vSphere Cluster Name to process. - - -#Requires PS -Version 4.0 -#Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} -#> - -[CmdletBinding()] -param( - [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0)] - [ValidateNotNullorEmpty()] - [String] $Clustername - -) - -Begin { - $Validate = $True - - if (($myCluster = Get-Cluster -Name $Clustername).count -lt 1) { - $Validate = $False - thow "No Cluster '$myCluster' found!" - } - -} - -Process { - - $MyView = @() - if ($Validate -eq $True) { - - foreach ($myVMhost in ($myCluster | Get-VMHost)) { - - $esxcli2 = Get-ESXCLI -VMHost $myVMhost -V2 - $niclist = $esxcli2.network.nic.list.invoke() - - $nicdetails = @() - foreach ($nic in $niclist) { - - $args = $esxcli2.network.nic.get.createargs() - $args.nicname = $nic.name - $nicdetail = $esxcli2.network.nic.get.Invoke($args) - $nicdetails += $nicdetail - - } - ForEach ($nicdetail in $nicdetails){ - $NICReport = [PSCustomObject] @{ - Host = $myVMhost.Name - vmnic = $nicdetail.Name - LinkStatus = $nicdetail.LinkStatus - BusInfo = $nicdetail.driverinfo.BusInfo - Driver = $nicdetail.driverinfo.Driver - FirmwareVersion = $nicdetail.driverinfo.FirmwareVersion - DriverVersion = $nicdetail.driverinfo.Version - } - $MyView += $NICReport - } - - } - - $MyView - - } -} -} \ No newline at end of file diff --git a/Modules/Get-NewAndRemovedVMs.psm1 b/Modules/Get-NewAndRemovedVMs.psm1 deleted file mode 100644 index 4a2e3ba..0000000 --- a/Modules/Get-NewAndRemovedVMs.psm1 +++ /dev/null @@ -1,131 +0,0 @@ -function Get-NewAndRemovedVMs { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2016.12 ver 1.0 Base Release - =========================================================================== - External Code Sources: - https://github.com/alanrenouf/vCheck-vSphere - =========================================================================== - Tested Against Environment: - vSphere Version: 5.5 U2 - PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1 - PowerShell Version: 4.0, 5.0 - OS Version: Windows 8.1, Server 2012 R2 - =========================================================================== - Keywords vSphere, VM - =========================================================================== - - .DESCRIPTION - This Function report newly created and deleted VMs by Cluster. - - .Example - Get-NewAndRemovedVMs -ClusterName Cluster* | ft -AutoSize - - .Example - Get-NewAndRemovedVMs -ClusterName Cluster01 -Days 90 - - .PARAMETER ClusterName - Name or Wildcard of your vSphere Cluster Name(s) to report. - - .PARAMETER Day - Range in Days to report. - - -#Requires PS -Version 4.0 -#Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} -#> - -param( - [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0, HelpMessage = "Name or Wildcard of your vSphere Cluster Name to report")] - [ValidateNotNullorEmpty()] - [String]$ClusterName, - [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=1, HelpMessage = "Range in Days to report")] - [ValidateNotNullorEmpty()] - [String]$Days = "30" -) -Begin { - function Get-VIEventPlus { - - param( - [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]]$Entity, - [string[]]$EventType, - [DateTime]$Start, - [DateTime]$Finish = (Get-Date), - [switch]$Recurse, - [string[]]$User, - [Switch]$System, - [string]$ScheduledTask, - [switch]$FullMessage = $false, - [switch]$UseUTC = $false - ) - - process { - $eventnumber = 100 - $events = @() - $eventMgr = Get-View EventManager - $eventFilter = New-Object VMware.Vim.EventFilterSpec - $eventFilter.disableFullMessage = ! $FullMessage - $eventFilter.entity = New-Object VMware.Vim.EventFilterSpecByEntity - $eventFilter.entity.recursion = &{if($Recurse){"all"}else{"self"}} - $eventFilter.eventTypeId = $EventType - if($Start -or $Finish){ - $eventFilter.time = New-Object VMware.Vim.EventFilterSpecByTime - if($Start){ - $eventFilter.time.beginTime = $Start - } - if($Finish){ - $eventFilter.time.endTime = $Finish - } - } - if($User -or $System){ - $eventFilter.UserName = New-Object VMware.Vim.EventFilterSpecByUsername - if($User){ - $eventFilter.UserName.userList = $User - } - if($System){ - $eventFilter.UserName.systemUser = $System - } - } - if($ScheduledTask){ - $si = Get-View ServiceInstance - $schTskMgr = Get-View $si.Content.ScheduledTaskManager - $eventFilter.ScheduledTask = Get-View $schTskMgr.ScheduledTask | - where {$_.Info.Name -match $ScheduledTask} | - Select -First 1 | - Select -ExpandProperty MoRef - } - if(!$Entity){ - $Entity = @(Get-Folder -NoRecursion) - } - $entity | %{ - $eventFilter.entity.entity = $_.ExtensionData.MoRef - $eventCollector = Get-View ($eventMgr.CreateCollectorForEvents($eventFilter)) - $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) - while($eventsBuffer){ - $events += $eventsBuffer - $eventsBuffer = $eventCollector.ReadNextEvents($eventnumber) - } - $eventCollector.DestroyCollector() - } - if (-not $UseUTC) - { - $events | % { $_.createdTime = $_.createdTime.ToLocalTime() } - } - - $events - } -} -} - -process { - $result = Get-VIEventPlus -Start ((get-date).adddays(-$Days)) -EventType @("VmCreatedEvent", "VmBeingClonedEvent", "VmBeingDeployedEvent","VmRemovedEvent") - $sortedResult = $result | Select CreatedTime, @{N='Cluster';E={$_.ComputeResource.Name}}, @{Name="VMName";Expression={$_.vm.name}}, UserName, @{N='Type';E={$_.GetType().Name}}, FullFormattedMessage | Sort CreatedTime - $sortedResult | where {$_.Cluster -like $ClusterName} -} -} \ No newline at end of file diff --git a/Modules/Get-VMmaxIOPS.psm1 b/Modules/Get-VMmaxIOPS.psm1 deleted file mode 100644 index 27af1ad..0000000 --- a/Modules/Get-VMmaxIOPS.psm1 +++ /dev/null @@ -1,114 +0,0 @@ -function Get-VMmaxIOPS { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2016.10 ver 1.0 Base Release - 2016.11 ver 1.1 Added vSphere 6.5 Support, New Counters, More Error Handling - =========================================================================== - External Code Sources: - http://www.lucd.info/2011/04/22/get-the-maximum-iops/ - https://communities.vmware.com/thread/485386 - =========================================================================== - Tested Against Environment: - vSphere Version: 5.5 U2, 6.5 - PowerCLI Version: PowerCLI 6.3 R1, 6.5 R1 - PowerShell Version: 4.0, 5.0 - OS Version: Windows 8.1, Windows Server 2012 R2 - =========================================================================== - Keywords vSphere, ESXi, VM, Storage - =========================================================================== - - .DESCRIPTION - This Function will Create a VM Disk IOPS Report - - .Example - Get-VM TST* | Get-VMmaxIOPS -Minutes 60 | FT -Autosize - - .Example - $SampleVMs = Get-VM "TST*" - Get-VMmaxIOPS -VMs $SampleVMs -Minutes 60 - - .PARAMETER VMs - Specify the VMs - - .PARAMETER Minutes - Specify the Minutes to report (10080 is one Week) - -#Requires PS -Version 4.0 -#Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} -#> - -[CmdletBinding()] -param( - [Parameter(Mandatory=$true, ValueFromPipeline=$True, Position=0)] - [ValidateNotNullorEmpty()] - [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]] $VMs, - [Parameter(Mandatory=$false, Position=1, HelpMessage = "Specify the Minutes to report (10080 is one Week)")] - [ValidateNotNullorEmpty()] - [int] $Minutes = 30 -) -Begin { - # none - } -Process { - if ($_.PowerState -eq "PoweredOn") { - #region: Global Definitions - [int]$TimeRange = "-" + $Minutes - #endregion - - #region: Creating VM Stats - Write-Verbose "$(Get-Date -Format G) Create VM Stats..." - $VMMetrics = "virtualdisk.numberwriteaveraged.average","virtualdisk.numberreadaveraged.average" - $Start = (Get-Date).AddMinutes($TimeRange) - $stats = Get-Stat -Realtime -Stat $VMMetrics -Entity $VMs -Start $Start -Verbose:$False - Write-Verbose "$(Get-Date -Format G) Create VM Stats completed" - #endregion - - #region: Creating HD-Tab - Write-Verbose "$(Get-Date -Format G) Create HD Tab..." - $hdTab = @{} - foreach($hd in (Get-Harddisk -VM $VMs)){ - $controllerKey = $hd.Extensiondata.ControllerKey - $controller = $hd.Parent.Extensiondata.Config.Hardware.Device | where{$_.Key -eq $controllerKey} - $hdTab[$hd.Parent.Name + "/scsi" + $controller.BusNumber + ":" + $hd.Extensiondata.UnitNumber] = $hd.FileName.Split(']')[0].TrimStart('[') - } - Write-Verbose "$(Get-Date -Format G) Create HD Tab completed" - #endregion - - #region: Creating Reports - Write-Verbose "$(Get-Date -Format G) Create Report..." - $reportPerf = @() - $reportPerf = $stats | Group-Object -Property {$_.Entity.Name},Instance | %{ - New-Object PSObject -Property @{ - VM = $_.Values[0] - Disk = $_.Values[1] - IOPSWriteAvg = [math]::round( ($_.Group | ` - where{$_.MetricId -eq "virtualdisk.numberwriteaveraged.average"} | ` - Measure-Object -Property Value -Average).Average,2) - IOPSReadAvg = [math]::round( ($_.Group | ` - where{$_.MetricId -eq "virtualdisk.numberreadaveraged.average"} | ` - Measure-Object -Property Value -Average).Average,2) - Datastore = $hdTab[$_.Values[0] + "/"+ $_.Values[1]] - } - } - Write-Verbose "$(Get-Date -Format G) Create Report completed" - #endregion - - - } - Else { - Write-Error "VM $($_.Name) is Powered Off! Processing Skipped" - } - $reportPerf | Select-Object VM, Disk, Datastore, IOPSWriteAvg, IOPSReadAvg - } - -End { - # none - } - -} \ No newline at end of file diff --git a/Modules/Konfig-ESXi.psm1 b/Modules/Konfig-ESXi.psm1 deleted file mode 100644 index f14386a..0000000 --- a/Modules/Konfig-ESXi.psm1 +++ /dev/null @@ -1,234 +0,0 @@ -function Konfig-ESXi { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2016.12 ver 1.0 Base Release - 2016.12 ver 1.1 ESXi 6.5 Tests, Minor enhancements - =========================================================================== - External Code Sources: - Function My-Logger : http://www.virtuallyghetto.com/ - =========================================================================== - Tested Against Environment: - vSphere Version: ESXi 5.5 U2, ESXi 6.5 - PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1 - PowerShell Version: 4.0, 5.0 - OS Version: Windows 8.1, Server 2012 R2 - Keyword: ESXi, NTP, SSH, Syslog, SATP, - =========================================================================== - - .DESCRIPTION - This Function sets the Basic settings for a new ESXi. - - * NTP - * SSH - * Syslog - * Power Management - * HP 3PAR SATP/PSP Rule - * ... - - .Example - Konfig-ESXi -VMHost myesxi.lan.local -NTP 192.168.2.1, 192.168.2.2 -syslog "udp://loginsight.lan.local:514" - - .PARAMETER VMHost - Host to configure. - - .PARAMETER NTP - NTP Server(s) to set. - - .PARAMETER Syslog - Syslog Server to set, e.g. "udp://loginsight.lan.local:514" - - DNS Name must be resolvable! - - -#Requires PS -Version 4.0 -#Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} -#> - -[CmdletBinding()] -param( - [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0)] - [String] $VMHost, - [Parameter(Mandatory=$true, ValueFromPipeline=$False, Position=1)] - [array]$NTP, - [Parameter(Mandatory=$true, ValueFromPipeline=$False, Position=2)] - [String] $syslog - -) - -Begin { - Function My-Logger { - param( - [Parameter(Mandatory=$true)] - [String]$message - ) - - $timeStamp = Get-Date -Format "MM-dd-yyyy_hh-mm-ss" - - Write-Host -NoNewline -ForegroundColor White "[$timestamp]" - Write-Host -ForegroundColor Green " $message" - } - function Set-MyESXiOption { - [CmdletBinding()] - param( - [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0)] - [String] $Name, - [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=1)] - [String] $Value - ) - process { - $myESXiOption = Get-AdvancedSetting -Entity $ESXiHost -Name $Name - if ($myESXiOption.Value -ne $Value) { - My-Logger " Setting ESXi Option $Name to Value $Value" - $myESXiOption | Set-AdvancedSetting -Value $Value -Confirm:$false | Out-Null - } - else { - My-Logger " ESXi Option $Name already has Value $Value" - } - } - } -} - -Process { - $Validate = $True - - #region: Start vCenter Connection - My-Logger "Starting to Process ESXi Server Connection to $VMHost ..." - if (($global:DefaultVIServers).count -gt 0) { - Disconnect-VIServer -Force -Confirm:$False -ErrorAction SilentlyContinue - } - $VIConnection = Connect-VIServer -Server $VMHost - if (-not $VIConnection.IsConnected) { - Write-Error "ESXi Connection Failed." - $Validate = $False - } - elseif ($VIConnection.ProductLine -ne "EmbeddedEsx") { - Write-Error "Connencted System is not an ESXi." - $Validate = $False - } - else { - $ESXiHost = Get-VMHost - My-Logger "Connected ESXi Version: $($ESXiHost.Version) $($ESXiHost.Build) " - } - #endregion - - if ($Validate -eq $True) { - - #region: Enable SSH and disable SSH Warning - $SSHService = $ESXiHost | Get-VMHostService | where {$_.Key -eq 'TSM-SSH'} - My-Logger "Starting SSH Service..." - if($SSHService.Running -ne $True){ - Start-VMHostService -HostService $SSHService -Confirm:$false | Out-Null - } - else { - My-Logger " SSH Service is already running" - } - My-Logger "Setting SSH Service to Automatic Start..." - if($SSHService.Policy -ne "automatic"){ - Set-VMHostService -HostService $SSHService -Policy "Automatic" | Out-Null - } - else { - My-Logger " SSH Service is already set to Automatic Start" - } - My-Logger "Disabling SSH Warning..." - Set-MyESXiOption -Name "UserVars.SuppressShellWarning" -Value "1" - #endregion - - #region: Config NTP - My-Logger "Removing existing NTP Server..." - try { - $ESXiHost | Remove-VMHostNtpServer -NtpServer (Get-VMHostNtpServer) -Confirm:$false - } - catch [System.Exception] { - Write-Warning "Error during removing existing NTP Servers." - } - My-Logger "Setting new NTP Servers..." - foreach ($myNTP in $NTP) { - $ESXiHost | Add-VMHostNtpServer -ntpserver $myNTP -confirm:$False | Out-Null - } - - My-Logger "Configure NTP Service..." - $NTPService = $ESXiHost | Get-VMHostService| Where-Object {$_.key -eq "ntpd"} - if($NTPService.Running -eq $True){ - Stop-VMHostService -HostService $NTPService -Confirm:$false | Out-Null - } - if($NTPService.Policy -ne "on"){ - Set-VMHostService -HostService $NTPService -Policy "on" -confirm:$False | Out-Null - } - - My-Logger "Configure Local Time..." - $HostTimeSystem = Get-View $ESXiHost.ExtensionData.ConfigManager.DateTimeSystem - $HostTimeSystem.UpdateDateTime([DateTime]::UtcNow) - - My-Logger "Start NTP Service..." - Start-VMHostService -HostService $NTPService -confirm:$False | Out-Null - #endregion - - #region: Remove default PG - My-Logger "Checking for Default Port Group ..." - if ($defaultPG = $ESXiHost | Get-VirtualSwitch -Name vSwitch0 | Get-VirtualPortGroup -Name "VM Network" -ErrorAction SilentlyContinue ){ - Remove-VirtualPortGroup -VirtualPortGroup $defaultPG -confirm:$False | Out-Null - My-Logger " Default PG Removed" - } - else { - My-Logger " No Default PG found" - } - #endregion - - #region: Configure Static HighPower - My-Logger "Setting PowerProfile to Static HighPower..." - try { - $HostView = ($ESXiHost | Get-View) - (Get-View $HostView.ConfigManager.PowerSystem).ConfigurePowerPolicy(1) - } - catch [System.Exception] { - Write-Warning "Error during Configure Static HighPower. See latest errors..." - } - #endregion - - #region: Conf Syslog - My-Logger "Setting Syslog Firewall Rule ..." - $SyslogFW = ($ESXiHost | Get-VMHostFirewallException | where {$_.Name -eq 'syslog'}) - if ($SyslogFW.Enabled -eq $False ){ - $SyslogFW | Set-VMHostFirewallException -Enabled:$true -Confirm:$false | Out-Null - My-Logger " Syslog Firewall Rule enabled" - } - else { - My-Logger " Syslog Firewall Rule already enabled" - } - My-Logger "Setting Syslog Server..." - Set-MyESXiOption -Name "Syslog.global.logHost" -Value $syslog - #endregion - - #region: Change Disk Scheduler - My-Logger "Changing Disk Scheduler..." - Set-MyESXiOption -Name "Disk.SchedulerWithReservation" -Value "0" - #endregion - - #region: Configure HP 3PAR SATP/PSP Rule - My-Logger "Configure HP 3PAR SATP/PSP Rule" - $esxcli2 = Get-ESXCLI -VMHost $ESXiHost -V2 - $arguments = $esxcli2.storage.nmp.satp.rule.add.CreateArgs() - $arguments.satp = "VMW_SATP_ALUA" - $arguments.psp = "VMW_PSP_RR" - $arguments.pspoption = "iops=100" - $arguments.claimoption = "tpgs_on" - $arguments.vendor = "3PARdata" - $arguments.model = "VV" - $arguments.description = "HP 3PAR custom SATP Claimrule" - try { - $esxcli2.storage.nmp.satp.rule.add.Invoke($arguments) - } - catch { - Write-Warning "Error during Configure HP 3PAR SATP/PSP Rule. See latest errors..." - } - #endregion - - } - } -} diff --git a/Modules/PSvLIMessage.psm1 b/Modules/PSvLIMessage.psm1 deleted file mode 100644 index 9cab209..0000000 --- a/Modules/PSvLIMessage.psm1 +++ /dev/null @@ -1,123 +0,0 @@ -add-type @" - using System.Net; - using System.Security.Cryptography.X509Certificates; - public class TrustAllCertsPolicy : ICertificatePolicy { - public bool CheckValidationResult( - ServicePoint srvPoint, X509Certificate certificate, - WebRequest request, int certificateProblem) { - return true; - } - } -"@ -[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy - -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Organization: Private - Personal Blog: mycloudrevolution.com - Twitter: @vMarkus_K - =========================================================================== - Tested Against Environment: - vRealize Log Insight 3.3.1 - PowerShell Version: 4.0, 5.0 - OS Version: Windows 8.1, Server 2012 R2 - Keyword: vRealize, RestAPI - - Dependencies: - PowerCLI Version: PowerCLI 6.3 R1 - - .SYNOPSIS - Push Messages to VMware vRealize Log Insight. - - .DESCRIPTION - Creates a Messages in VMware vRealize Log Insight via the Ingestion API - - .EXAMPLE - Push-vLIMessage -vLIServer "loginsight.lan.local" -vLIAgentID "12862842-5A6D-679C-0E38-0E2BE888BB28" -Text "My Test" - - .EXAMPLE - Push-vLIMessage -vLIServer "loginsight.lan.local" -vLIAgentID "12862842-5A6D-679C-0E38-0E2BE888BB28" -Text "My Test" -Hostname MyTEST -FieldName myTest -FieldContent myTest - - .PARAMETER vLIServer - Specify the FQDN of your vRealize Log Insight Appliance - - .PARAMETER vLIAgentID - Specify the vRealize Log Insight Agent ID, e.g. "12862842-5A6D-679C-0E38-0E2BE888BB28" - - .PARAMETER Text - Specify the Event Text - - .PARAMETER Hostname - Specify the Hostanme displayed in vRealize Log Insight - - .PARAMETER FieldName - Specify the a Optional Field Name for vRealize Log Insight - - .PARAMETER FieldContent - Specify the a Optional FieldContent for the Field in -FieldName for vRealize Log Insight - If FielName is missing and FieldContent is given, it will be ignored - - #Requires PS -Version 3.0 - - #> -function Push-vLIMessage { - - [cmdletbinding()] - param ( - [parameter(Mandatory=$true)] - [string]$Text, - [parameter(Mandatory=$true)] - [string]$vLIServer, - [parameter(Mandatory=$true)] - [string]$vLIAgentID, - [parameter(Mandatory=$false)] - [string]$Hostname = $env:computername, - [parameter(Mandatory=$false)] - [string]$FieldName, - [parameter(Mandatory=$false)] - [string]$FieldContent = "" - ) - Process { - $Field_vLI = [ordered]@{ - name = "PS_vLIMessage" - content = "true" - } - $Field_HostName = [ordered]@{ - name = "hostname" - content = $Hostname - } - - $Fields = @($Field_vLI, $Field_HostName) - - if ($FieldName) { - $Field_Custom = [ordered]@{ - name = $FieldName - content = $FieldContent - } - $Fields += @($Field_Custom) - } - - $Restcall = @{ - messages = ([Object[]]([ordered]@{ - text = ($Text) - fields = ([Object[]]$Fields) - })) - } | convertto-json -Depth 4 - - $Resturl = ("http://" + $vLIServer + ":9000/api/v1/messages/ingest/" + $vLIAgentID) - try - { - $Response = Invoke-RestMethod $Resturl -Method Post -Body $Restcall -ContentType 'application/json' -ErrorAction stop - Write-Information "REST Call to Log Insight server successful" - Write-Verbose $Response - } - catch - { - Write-Error "REST Call failed to Log Insight server" - Write-Verbose $error[0] - Write-Verbose $Resturl - } - } -} \ No newline at end of file diff --git a/Modules/ProactiveHA.psm1 b/Modules/ProactiveHA.psm1 deleted file mode 100644 index ea4e92f..0000000 --- a/Modules/ProactiveHA.psm1 +++ /dev/null @@ -1,468 +0,0 @@ -Function New-PHAProvider { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to register a new Proactive HA Provider with vCenter Server - .PARAMETER ProviderName - Name of ProactiveHA Provider - .PARAMETER ComponentType - Name of a supported ComponentType that ProactiveHA supports (Fan, Memory, Network, Power or Storage) - .PARAMETER ComponentDescription - Description of the health check for the given component - .PARAMETER ComponentId - Unique identifier for the given component within a ProactiveHA Provider - .EXAMPLE - New-PHAProvider -ProviderName "virtuallyGhetto" -ComponentType Power -ComponentDescription "Simulated ProactiveHA Provider" -ComponentId "Power" -#> - param( - [Parameter(Mandatory=$true)][String]$ProviderName, - [Parameter(Mandatory=$true)][ValidateSet("Fan","Memory","Network","Power","Storage")][String]$ComponentType, - [Parameter(Mandatory=$true)][String]$ComponentDescription, - [Parameter(Mandatory=$true)][String]$ComponentId - ) - Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" - Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" - Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" - Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" - - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - $healthInfo = [VMware.Vim.HealthUpdateInfo] @{ - ComponentType = $ComponentType - description = $ComponentDescription - Id = $ComponentId - } - - try { - Write-Host "`nRegistering new Proactive HA Provider $ProviderName ..." - $providerId = $healthManager.RegisterHealthUpdateProvider($ProviderName,$healthInfo) - } catch { - Write-host -ForegroundColor Red $Error[0].Exception - } -} - -Function Get-PHAProvider { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to return list of all Proactive HA Providers registered with vCenter Server - .EXAMPLE - Get-PHAProvider -#> - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - $healthProviderResults = @() - $hpIDs = $healthManager.QueryProviderList() - - foreach ($hpID in $hpIDs) { - $hpName = $healthManager.QueryProviderName($hpID) - $hpConfig = $healthManager.QueryHealthUpdateInfos($hpID) - - $hp = [pscustomobject] @{ - ProviderName = $hpName - ProviderID = $hpID - ComponentType = $hpConfig.componentType - ComponentID = $hpConfig.id - Description = $hpConfig.description - } - $healthProviderResults+=$hp - } - $healthProviderResults -} - -Function Remove-PHAProvider { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to remove a registered Proactive HA Provider from vCenter Server - .PARAMETER ProviderId - The ProactiveHA provider ID (retrieved from Get-PHAProvider) to unregister - .EXAMPLE - Remove-PHAProvider -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" -#> - param( - [Parameter(Mandatory=$true)][String]$ProviderId - ) - - Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" - Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" - Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" - Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" - - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - try { - Write-Host "`nUnregistering Proactive HA Provider $ProviderId ... " - $healthManager.UnregisterHealthUpdateProvider($providerId) - } catch { - if($Error[0].Exception.InnerException.MethodFault.getType().Name -eq "InvalidState") { - Write-host -ForegroundColor Red "The Proactive HA Provider is still in use, please disable it before unregistering" - } else { - Write-host -ForegroundColor Red $Error[0].Exception - } - } -} - -Function Set-PHAConfig { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to enable/disable Proactive HA for vSphere Cluster - .PARAMETER Cluster - Name of the vSphere Cluster to enable Proactive HA - .PARAMETER ProviderId - Proactive HA Provider ID to enable in vSphere Cluster - .PARAMETER ClusterMode - Whether Proactive HA should be "Automated" or "Manual" for actions it will take - .PARAMETER ModerateRemediation - Type of operation (Maintenance Mode or Quaratine Mode) to perform when a Moderate issue is observed - .PARAMETER SevereRemediation - Type of operation (Maintenance Mode or Quaratine Mode) to perform when a Severe issue is observed - .EXAMPLE - Set-PHAConfig -Cluster VSAN-Cluster -Enabled -ClusterMode Automated -ModerateRemediation QuarantineMode -SevereRemediation QuarantineMode -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" - .EXAMPLE - Set-PHAConfig -Cluster VSAN-Cluster -Disabled -ProviderID "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" -#> - param( - [Parameter(Mandatory=$true)][String]$ProviderId, - [Parameter(Mandatory=$true)][String]$Cluster, - [Parameter(Mandatory=$false)][ValidateSet("Automated","Manual")]$ClusterMode="Manual", - [Parameter(Mandatory=$false)][ValidateSet("MaintenanceMode","QuarantineMode")]$ModerateRemediation="QuarantineMode", - [Parameter(Mandatory=$false)][ValidateSet("MaintenanceMode","QuarantineMode")]$SevereRemediation="QuarantineMode", - [Switch]$Enabled, - [Switch]$Disabled - ) - - $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,Host,ConfigurationEx -Filter @{"Name" = $Cluster} - - if($ClusterView -eq $null) { - Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." - break - } - - $vmhosts = $ClusterView.host - - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - if($Enabled) { - try { - $entities = @() - foreach ($vmhost in $vmhosts) { - if(-not $healthManager.HasMonitoredEntity($ProviderId,$vmhost)) { - $entities += $vmhost - } - } - - Write-Host "Enabling Proactive HA monitoring for all ESXi hosts in cluster ..." - $healthManager.AddMonitoredEntities($ProviderId,$entities) - } catch { - Write-host -ForegroundColor Red $Error[0].Exception - } - - try { - $healthProviders = @() - - # Make sure not to remove existing ProactiveHA providers - if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { - $currentHPs = $ClusterView.ConfigurationEx.infraUpdateHaConfig.providers - foreach ($currentHP in $currentHPs) { - $healthProviders+=$currentHP - } - if(-not ($healthProviders -contains $ProviderID)) { - $healthProviders+=$ProviderId - } - } else { - $healthProviders+=$ProviderId - } - - $PHASpec = [VMware.Vim.ClusterInfraUpdateHaConfigInfo] @{ - enabled = $true - behavior = $ClusterMode - moderateRemediation = $ModerateRemediation - severeRemediation = $SevereRemediation - providers = $healthProviders - } - - $spec = [VMware.Vim.ClusterConfigSpecEx] @{ - infraUpdateHaConfig = $PHASpec - } - - Write-Host "Enabling Proactive HA Provider $ProviderId on $Cluster ..." - $task = $ClusterView.ReconfigureComputeResource_Task($spec,$True) - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task | Out-Null - } catch { - Write-host -ForegroundColor Red $Error[0].Exception - } - } - - if($Disabled) { - foreach ($vmhost in $vmhosts) { - if($vmhost.runtime.inQuarantineMode) { - Write-Host -ForegroundColor Red $vmhost.name " is currently still in Quaratine Mode, please remediate this before disabling Proactive HA" - break - } - } - - try { - $healthProviders = @() - - # Make sure not to remove existing ProactiveHA providers - if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { - $currentHPs = $ClusterView.ConfigurationEx.infraUpdateHaConfig.providers - foreach ($currentHP in $currentHPs) { - if($currentHP -ne $ProviderId) { - $healthProviders+=$currentHP - } - } - } - - $PHASpec = [VMware.Vim.ClusterInfraUpdateHaConfigInfo] @{ - enabled = $true - behavior = $ClusterMode - moderateRemediation = $ModerateRemediation - severeRemediation = $SevereRemediation - providers = $healthProviders - } - - $spec = [VMware.Vim.ClusterConfigSpecEx] @{ - infraUpdateHaConfig = $PHASpec - } - - Write-Host "Disabling Proactive HA Provider $ProviderId on $Cluster ..." - $task = $ClusterView.ReconfigureComputeResource_Task($spec,$True) - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task | Out-Null - } catch { - Write-host -ForegroundColor Red $Error[0].Exception - } - - $ClusterView.UpdateViewData() - - try { - $entities = @() - foreach ($vmhost in $vmhosts) { - if($healthManager.HasMonitoredEntity($ProviderId,$vmhost)) { - $entities += $vmhost - } - } - - Write-Host "Disabling Proactive HA monitoring for all ESXi hosts in cluster ..." - $healthManager.RemoveMonitoredEntities($ProviderId,$entities) - } catch { - Write-host -ForegroundColor Red $Error[0].Exception - } - } -} - -Function Get-PHAConfig { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to retrieve Proactive HA configuration for a vSphere Cluster - .PARAMETER Cluster - Name of the vSphere Cluster to check Proactive HA configuration - .EXAMPLE - Get-PHAConfig -Cluster VSAN-Cluster -#> - param( - [Parameter(Mandatory=$true)][String]$Cluster - ) - - $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,ConfigurationEx -Filter @{"Name" = $Cluster} - - if($ClusterView -eq $null) { - Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." - break - } - - if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - $phSettings = $ClusterView.ConfigurationEx.InfraUpdateHaConfig - $providers = $ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers - $healthProviders = @() - foreach ($provider in $providers) { - $providerName = $healthManager.QueryProviderName($provider) - $healthProviders+=$providerName - } - - $pHAConfig = [pscustomobject] @{ - Enabled = $phSettings.Enabled - ClusterMode = $phSettings.behavior - ModerateRemediation = $phSettings.ModerateRemediation - SevereRemediation = $phSettings.SevereRemediation - HealthProviders = $healthProviders - } - $pHAConfig - } else { - Write-Host "Proactive HA has not been configured on this vSphere Cluster" - } -} - -Function Get-PHAHealth { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to retrieve the Proactive HA health info for all ESXi hosts in vSphere Cluster - .PARAMETER Cluster - Name of the vSphere Cluster to check Proactive HA health information - .EXAMPLE - Get-PHAHealth -Cluster VSAN-Cluster -#> - param( - [Parameter(Mandatory=$true)][String]$Cluster - ) - - $ClusterView = Get-View -ViewType ClusterComputeResource -Property Name,ConfigurationEx -Filter @{"Name" = $Cluster} - - if($ClusterView -eq $null) { - Write-Host -ForegroundColor Red "Unable to find vSphere Cluster $cluster ..." - break - } - - if($ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers -ne $null) { - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - $providers = $ClusterView.ConfigurationEx.InfraUpdateHaConfig.Providers - - foreach ($provider in $providers) { - $providerName = $healthManager.QueryProviderName($provider) - $healthUpdates = $healthManager.QueryHealthUpdates($provider) - - $healthResults = @() - Write-Host -NoNewline -ForegroundColor Magenta "Health summary for Proactive HA Provider $providerName`:`n" - foreach ($healthUpdate in $healthUpdates) { - $vmhost = Get-View $healthUpdate.Entity - - $hr = [PSCustomObject] @{ - Entity = $vmhost.name - Status = $healthUpdate.status - HealthComponentId = $healthUpdate.HealthUpdateInfoId - HealthUpdateId = $healthUpdate.Id - Remediation = $healthUpdate.Remediation - } - $healthResults+=$hr - } - $healthResults - } - } else { - Write-Host "Proactive HA has not been configured on this vSphere Cluster" - } -} - -Function New-PHASimulation { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .DESCRIPTION - Function to return VCHA Configuration - .PARAMETER ProviderId - The Proactive HA Provider ID that you like to simulate a health update from - .PARAMETER EsxiHost - The name of ESXi host to update the health on - .PARAMETER Component - The name of the matching component ID from Proactive HA Provider to simulate a health update from - .PARAMETER HealthStatus - The health value (green, yellow or red) for the given simulated health Update - .PARAMETER Remediation - The remediation message associated with simulated health update - .EXAMPLE - New-PHASimulation -EsxiHost vesxi65-4.primp-industries.com -Component Power -HealthStatus green -Remediation "" -ProviderId "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" - .EXAMPLE - New-PHASimulation -EsxiHost vesxi65-4.primp-industries.com -Component Power -HealthStatus red -Remediation "Please replace my virtual PSU" -ProviderId "52 85 22 c2 f2 6a e7 b9-fc ff 63 9e 10 81 00 79" -#> - param( - [Parameter(Mandatory=$true)][String]$ProviderId, - [Parameter(Mandatory=$true)][String]$EsxiHost, - [Parameter(Mandatory=$true)][String]$Component, - [Parameter(Mandatory=$true)][ValidateSet("green","red","yellow")][String]$HealthStatus, - [Parameter(Mandatory=$false)][String]$Remediation - ) - - Write-Host -ForegroundColor Red "`n******************** DISCLAIMER ********************" - Write-Host -ForegroundColor Red "**** THIS IS NOT INTENDED FOR PRODUCTION USE ****" - Write-Host -ForegroundColor Red "**** LEARNING PURPOSES ONLY ****" - Write-Host -ForegroundColor Red "******************** DISCLAIMER ********************`n" - - $vmhost = Get-View -ViewType HostSystem -Property Name -Filter @{"name" = $EsxiHost} - - if($vmhost -eq $null) { - Write-Host -ForegroundColor Red "`nUnable to find ESXi host $EsxiHost ..." - break - } - - $healthManager = Get-View $global:DefaultVIServer.ExtensionData.Content.HealthUpdateManager - - # Randomly generating an ID for Health Update - # In general, you would want to generate a specific ID - # which can be referenced between ProactiveHA Provider - # and VMware logs for troubleshooting purposes - $HealthUpdateID = "vghetto-" + (Get-Random -Minimum 1 -Maximum 100000) - - # All other Health Status can have a remediation message - # but for green, it must be an empty string or API call will fail - if($HealthStatus -eq "green") { - $Remediation = "" - } - - $healthUpdate = [VMware.Vim.HealthUpdate] @{ - Entity = $vmhost.moref - HealthUpdateInfoId = $Component - Id = $HealthUpdateId - Status = $HealthStatus - Remediation = $Remediation - } - - try { - Write-Host "`nSimulating Proactive HA Health Update to ..." - Write-Host "`tHost: $EsxiHost " - Write-Host -NoNewline "`tStatus: " - Write-Host -ForegroundColor $HealthStatus "$HealthStatus" - Write-Host "`tRemediation Messsage: $Remediation" - $healthManager.PostHealthUpdates($providerId,$healthUpdate) - } catch { - Write-host -ForegroundColor Red $Error[0].Exception - } -} \ No newline at end of file diff --git a/Modules/Recommend-Sizing.psm1 b/Modules/Recommend-Sizing.psm1 deleted file mode 100644 index 0075e43..0000000 --- a/Modules/Recommend-Sizing.psm1 +++ /dev/null @@ -1,227 +0,0 @@ -function Recommend-Sizing { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2016.11 ver 1.0 Base Release - 2016.11 ver 1.1 Optional Stats Collection - 2016.11 ver 1.2 VM Stats from Realtime Data and new Counters - =========================================================================== - External Code Sources: - http://www.lucd.info/2011/04/22/get-the-maximum-iops/ - https://communities.vmware.com/thread/485386 - =========================================================================== - Tested Against Environment: - vSphere Version: 5.5 U2, 6.0 - PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1 - PowerShell Version: 4.0, 5.0 - OS Version: Windows 8.1, Server 2012 R2 - =========================================================================== - Keywords vSphere, ESXi, VM, Storage, Sizing - =========================================================================== - - .DESCRIPTION - This Function collects Basic vSphere Informations for a Hardware Sizing Recommandation. Focus is in Compute Ressources. - - .Example - Recommend-Sizing -ClusterNames Cluster01, Cluster02 -Stats -StatsRange 60 -Verbose - - .Example - Recommend-Sizing -ClusterNames Cluster01, Cluster02 - - .Example - Recommend-Sizing -ClusterNames Cluster01 - - .PARAMETER ClusterNames - List of your vSphere Cluser Names to process. - - .PARAMETER Stats - Enables Stats Collection. - - Warning: At the moment this is only fully tested with vSphere 5.5 and vSphere 6.5! - - .PARAMETER StatsRange - Time Range in Minutes for the Stats Collection. - Default is 24h. - -#Requires PS -Version 4.0 -#Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} -#> - -[CmdletBinding()] -param( - [Parameter(Mandatory=$True, ValueFromPipeline=$False, Position=0)] - [Array] $ClusterNames, - [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=1, ParameterSetName = "Stats")] - [switch] $Stats, - [Parameter(Mandatory=$False, ValueFromPipeline=$False, Position=2, ParameterSetName = "Stats")] - [int] $StatsRange = 1440 - -) -Begin { - if ($Stats) { - Write-Warning "Stats Collection requested.`nAt the moment this is only fully tested with vSphere 5.5 and vSphere 6.5" - [int]$TimeRange = "-" + $StatsRange - } - - $Validate = $True - #region: Check Clusters - Write-Verbose "$(Get-Date -Format G) Starting Cluster Validation..." - foreach ($ClusterName in $ClusterNames) { - $TestCluster = Get-Cluster -Name $ClusterName -ErrorAction SilentlyContinue -Verbose:$False - if(!($TestCluster)){ - Write-Warning "No Custer found wth Name $ClusterName!" - $Validate = $False - } - elseif ($TestCluster.count -gt 1) { - Write-Warning "Multiple Custers found wth Name $ClusterName!`nUse a List of explicit Cluster Names: Recommend-Sizing -ClusterNames Cluster01, Cluster02 " - $Validate = $False - } - } - Write-Verbose "$(Get-Date -Format G) Cluster Validation completed" - #endregion -} - -Process { - $MyView = @() - if ($Validate -eq $True) { - foreach ($ClusterName in $ClusterNames) { - #region: Get Cluster Objects - Write-Verbose "$(Get-Date -Format G) Collect $ClusterName Cluster Objects..." - $Cluster = Get-Cluster -Name $ClusterName -Verbose:$False - $ClusterVMs = $Cluster | Get-VM -Verbose:$False - $ClusterVMsPoweredOn = $ClusterVMs | where {$_.PowerState -eq "PoweredOn"} - $ClusterDatastores = $Cluster | Get-Datastore -Verbose:$False - $ClusterHosts = $Cluster | Get-VMHost -Verbose:$False - $HostsAverageMemoryUsageGB = [math]::round( ($ClusterHosts | Measure-Object -Average -Property MemoryUsageGB).Average,1 ) - $HostsAverageMemoryUsage = $([math]::round( (($ClusterHosts | Measure-Object -Average -Property MemoryUsageGB).Average / ($ClusterHosts | Measure-Object -Average -Property MemoryTotalGB).Average) * 100,1 )) - $HostsAverageCpuUsageMhz = [math]::round( ($ClusterHosts | Measure-Object -Average -Property CpuUsageMhz).Average,1 ) - $HostsAverageCpuUsage = $([math]::round( (($ClusterHosts | Measure-Object -Average -Property CpuUsageMhz).Average / ($ClusterHosts | Measure-Object -Average -Property CpuTotalMhz).Average) * 100,1 )) - Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) Cluster Objects completed" - #endregion - - #region: CPU Calculation - Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) CPU Details..." - $VMvCPUs = ($ClusterVMs | Measure-Object -Sum -Property NumCpu).sum - $LogicalThreads = $Cluster.ExtensionData.Summary.NumCpuThreads - $CpuCores = $Cluster.ExtensionData.Summary.NumCpuCores - $vCPUpCPUratio = [math]::round( $VMvCPUs / $LogicalThreads,1 ) - Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) CPU Details completed." - #endregion - - #region: Memory Calculation - Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) Memory Details..." - $AllocatedVMMemoryGB = [math]::round( ($ClusterVMs | Measure-Object -Sum -Property MemoryGB).sum ) - $PhysicalMemory = [math]::round( $Cluster.ExtensionData.Summary.TotalMemory / 1073741824,1 ) - $MemoryUsage = [math]::round( ($AllocatedVMMemoryGB / $PhysicalMemory) * 100 ,1 ) - Write-Verbose "$(Get-Date -Format G) Collect $($Cluster.name) Memory Details completed" - #endregion - - if ($Stats) { - #region: Creating VM Stats - Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) VM Stats..." - $VMMetrics = "disk.numberwrite.summation","disk.numberread.summation","cpu.usage.average", "mem.usage.average" - $Start = (Get-Date).AddMinutes($TimeRange) - $VMStats = Get-Stat -Realtime -Stat $VMMetrics -Entity $ClusterVMsPoweredOn -Start $Start -Verbose:$False - Write-Verbose "$(Get-Date -Format G) Create $($Cluster.name) VM Stats completed" - #endregion - - #region: Creating VM Stats Report - Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Stats Report..." - $ReportVMPerf = @() - $ReportVMPerf = $VMStats | Group-Object -Property {$_.Entity.Name},Instance | %{ - New-Object PSObject -Property @{ - IOPSWriteAvg = ($_.Group | ` - where{$_.MetricId -eq "disk.numberwrite.summation"} | ` - Measure-Object -Property Value -Average).Average - IOPSReadAvg = ($_.Group | ` - where{$_.MetricId -eq "disk.numberread.summation"} | ` - Measure-Object -Property Value -Average).Average - CPUUsageAvg = ($_.Group | ` - where{$_.MetricId -eq "cpu.usage.average"} | ` - Measure-Object -Property Value -Average).Average - MEMUsageAvg = ($_.Group | ` - where{$_.MetricId -eq "mem.usage.average"} | ` - Measure-Object -Property Value -Average).Average - } - } - Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Stats Report completed" - #endregion - } - else { - Write-Verbose "$(Get-Date -Format G) Stats Collection skipped..." - } - - #region: Create VM Disk Space Report - Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Disk Space Report..." - $reportDiskSpace = @() - foreach ($ClusterVM in $ClusterVMs){ - $VMDKs = $ClusterVM | get-HardDisk -Verbose:$False - foreach ($VMDK in $VMDKs) { - if ($VMDK -ne $null){ - [int]$CapacityGB = $VMDK.CapacityKB/1024/1024 - $Report = [PSCustomObject] @{ - CapacityGB = $CapacityGB - } - $reportDiskSpace += $Report - } - } - } - Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) VM Disk Space Report completed" - #endregion - - #region: Create Datastore Space Report - Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) Datastore Space Report..." - $DatastoreReport = @($ClusterDatastores | Select-Object @{N="CapacityGB";E={[math]::Round($_.CapacityGB,2)}}, @{N="FreeSpaceGB";E={[math]::Round($_.FreeSpaceGB,2)}}, @{N="UsedSpaceGB";E={[math]::Round($_.CapacityGB - $_.FreeSpaceGB,2)}}) - Write-Verbose "$(Get-Date -Format G) Process $($Cluster.name) Datastore Space Report completed" - #endregion - - #region: Create Global Report - Write-Verbose "$(Get-Date -Format G) Process Global Report..." - $SizingReport = [PSCustomObject] @{ - Cluster = $Cluster.name - HAEnabled = $Cluster.HAEnabled - DrsEnabled = $Cluster.DrsEnabled - Hosts = $Cluster.ExtensionData.Summary.NumHosts - HostsAverageMemoryUsageGB = $HostsAverageMemoryUsageGB - HostsAverageMemoryUsage = "$HostsAverageMemoryUsage %" - HostsAverageCpuUsageMhz = $HostsAverageCpuUsageMhz - HostsAverageCpuUsage = "$HostsAverageCpuUsage %" - PhysicalCPUCores = $CpuCores - LogicalCPUThreads = $LogicalThreads - VMs = $ClusterVMs.count - ActiveVMs = $ClusterVMsPoweredOn.count - VMvCPUs = $VMvCPUs - vCPUpCPUratio = "$vCPUpCPUratio : 1" - PhysicalMemoryGB = $PhysicalMemory - AllocatedVMMemoryGB = $AllocatedVMMemoryGB - ClusterMemoryUsage = "$MemoryUsage %" - SumVMDiskSpaceGB = [math]::round( ($reportDiskSpace | Measure-Object -Sum -Property CapacityGB).sum, 1 ) - SumDatastoreSpaceGB = [math]::round( ($DatastoreReport | Measure-Object -Sum -Property CapacityGB).sum, 1 ) - SumDatastoreUsedSpaceGB = [math]::round( ($DatastoreReport | Measure-Object -Sum -Property UsedSpaceGB).sum, 1 ) - AverageVMIOPSWriteAvg = [math]::round( ($ReportVMPerf | Measure-Object -Average -Property IOPSWriteAvg).Average,1 ) - AverageVMIOPSReadAvg = [math]::round( ($ReportVMPerf | Measure-Object -Average -Property IOPSReadAvg).Average,1 ) - AverageVMCPUUsageAvg = "$([math]::round( ($ReportVMPerf | Measure-Object -Average -Property CPUUsageAvg).Average,1 )) %" - AverageVMMEMUsageAvg = "$([math]::round( ($ReportVMPerf | Measure-Object -Average -Property MEMUsageAvg).Average,1 )) %" - } - $MyView += $SizingReport - Write-Verbose "$(Get-Date -Format G) Process Global Report completed" - #endregion - } - - } - Else { - Write-Error "Validation Failed! Processing Skipped" - } - - } - - End { - $MyView - } - -} \ No newline at end of file diff --git a/Modules/Set-CBT.psm1 b/Modules/Set-CBT.psm1 deleted file mode 100644 index 784aebb..0000000 --- a/Modules/Set-CBT.psm1 +++ /dev/null @@ -1,111 +0,0 @@ -function Set-CBT { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2016.11 ver 1.0 Base Release - =========================================================================== - External Code Sources: - http://wahlnetwork.com/2015/12/01/change-block-tracking-cbt-powercli/ - =========================================================================== - Tested Against Environment: - vSphere Version: 5.5 U2 - PowerCLI Version: PowerCLI 6.3 R1 - PowerShell Version: 4.0 - OS Version: Windows Server 2012 R2 - =========================================================================== - Keywords vSphere, ESXi, VM, Storage, CBT, Backup - =========================================================================== - - .DESCRIPTION - This Function enables or disables CBT. - - .Example - Get-VN TST* | Set-CBT -DisableCBT - - .Example - Get-VN TST* | Set-CBT -EnableCBT - - .PARAMETER DisableCBT - Disables CBT for any VMs found with it enabled - - .PARAMETER EnableCBT - Enables CBT for any VMs found with it disabled - -#Requires PS -Version 4.0 -#Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} -#> - - [CmdletBinding()] - param( - [Parameter(Mandatory=$True, ValueFromPipeline=$True, Position=0, HelpMessage = "VMs to process")] - [ValidateNotNullorEmpty()] - [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]] $myVMs, - [Parameter(Mandatory = $False,ValueFromPipeline=$False, Position = 1, HelpMessage = "Enables CBT for any VMs found with it disabled", ParameterSetName = "EnableCBT")] - [ValidateNotNullorEmpty()] - [Switch]$EnableCBT, - [Parameter(Mandatory = $False,ValueFromPipeline=$False, Position = 1, HelpMessage = "Disables CBT for any VMs found with it enabled", ParameterSetName = "DisableCBT")] - [ValidateNotNullorEmpty()] - [Switch]$DisableCBT - ) -Process { - - $vmconfigspec = New-Object -TypeName VMware.Vim.VirtualMachineConfigSpec - Write-Verbose -Message "Walking through given VMs" - foreach($myVM in $myVMs) - { - if ($DisableCBT -and $myVM.ExtensionData.Config.ChangeTrackingEnabled -eq $true -and $myVM.ExtensionData.Snapshot -eq $null) - { - try - { - Write-Verbose -Message "Reconfiguring $($myVM.name) to disable CBT" -Verbose - $vmconfigspec.ChangeTrackingEnabled = $false - $myVM.ExtensionData.ReconfigVM($vmconfigspec) - - if ($myVM.PowerState -eq "PoweredOn" ) { - Write-Verbose -Message "Creating a snapshot on $($myVM.name) to clear CBT file" -Verbose - $SnapShot = New-Snapshot -VM $myVM -Name "CBT Cleanup" - - Write-Verbose -Message "Removing snapshot on $($myVM.name)" -Verbose - $SnapShot| Remove-Snapshot -Confirm:$false - } - - } - catch - { - throw $myVM - } - } - elseif ($EnableCBT -and $myVM.ExtensionData.Config.ChangeTrackingEnabled -eq $false -and $myVM.ExtensionData.Snapshot -eq $null) - { - Write-Verbose -Message "Reconfiguring $($myVM.name) to enable CBT" -Verbose - $vmconfigspec.ChangeTrackingEnabled = $true - $myVM.ExtensionData.ReconfigVM($vmconfigspec) - - if ($myVM.PowerState -eq "PoweredOn" ) { - Write-Verbose -Message "Creating a snapshot on $($myVM.name) to Create CBT file" -Verbose - $SnapShot = New-Snapshot -VM $myVM -Name "CBT Cleanup" - - Write-Verbose -Message "Removing snapshot on $($myVM.name)" -Verbose - $SnapShot | Remove-Snapshot -Confirm:$false - } - } - else - { - if ($myVM.ExtensionData.Snapshot -ne $null -and $EnableCBT) - { - Write-Warning -Message "Skipping $($myVM.name) - Snapshots found" - } - elseif ($myVM.ExtensionData.Snapshot -ne $null -and $DisableCBT) - { - Write-Warning -Message "Skipping $($myVM.name) - Snapshots found" - } - } - } - - } -} diff --git a/Modules/Start-UNMAP.psm1 b/Modules/Start-UNMAP.psm1 deleted file mode 100644 index a8e9896..0000000 --- a/Modules/Start-UNMAP.psm1 +++ /dev/null @@ -1,99 +0,0 @@ -function Start-UNMAP { -<# - .SYNOPSIS - Process SCSI UNMAP on VMware Datastores - - .DESCRIPTION - This Function will process SCSI UNMAP on VMware Datastores via ESXCLI -V2 - - .Example - Start-UNMAP -ClusterName myCluster -DSWildcard *RAID5* - - .Example - Start-UNMAP -ClusterName myCluster -DSWildcard *RAID5* -Verbose -WhatIf - - .Notes - NAME: Start-UNMAP.psm1 - AUTHOR: Markus Kraus - LASTEDIT: 23.09.2016 - VERSION: 1.0 - KEYWORDS: VMware, vSphere, ESXi, SCSI, VAAI, UNMAP - - .Link - http://mycloudrevolution.com/ - - #Requires PS -Version 4.0 - #Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} - #> - - [CmdletBinding(SupportsShouldProcess = $true,ConfirmImpact='High')] - param( - [Parameter(Mandatory=$true, Position=0)] - [String]$ClusterName, - [Parameter(Mandatory=$true, Position=1)] - [String]$DSWildcard - ) - Process { - $Validate = $true - #region: PowerCLI Session Timeout - Write-Verbose "Set Session Timeout ..." - $initialTimeout = (Get-PowerCLIConfiguration -Scope Session).WebOperationTimeoutSeconds - Set-PowerCLIConfiguration -Scope Session -WebOperationTimeoutSeconds -1 -Confirm:$False | Out-Null - #endregion - - #region: Get Cluster - $Cluster = Get-Cluster -Name $ClusterName -ErrorAction SilentlyContinue - Write-Verbose "vSphere Cluster: $Cluster" - if (!$Cluster){Write-Error "No Cluster found!"; $Validate = $false} - #endregion - - #region: Get Hosts - $ClusterHosts = $Cluster | Get-VMHost -ErrorAction SilentlyContinue | where {$_.ConnectionState -eq "Connected" -and $_.PowerState -eq "PoweredOn"} - Write-Verbose "vSphere Cluster Hosts: $ClusterHosts" - if (!$ClusterHosts){Write-Error "No Hosts found!"; $Validate = $false} - #endregion - - #region: Get Datastores - $ClusterDataStores = $Cluster | Get-Datastore -ErrorAction SilentlyContinue | where {$_.Name -like $DSWildcard -and $_.State -eq "Available" -and $_.Accessible -eq "True"} - Write-Verbose "vSphere Cluster Datastores: $ClusterDataStores" - if (!$ClusterDataStores){Write-Error "No Datastores found!"; $Validate = $false} - #endregion - - #region: Process Datastores - if ($Validate -eq $true) { - Write-Verbose "Starting Loop..." - foreach ($ClusterDataStore in $ClusterDataStores) { - Write-Verbose "vSphere Datastore to Process: $ClusterDataStore" - $myHost = $ClusterHosts[(Get-Random -Maximum ($ClusterHosts).count)] - Write-Verbose "vSphere Host to Process: $myHost" - $esxcli2 = $myHost | Get-ESXCLI -V2 - $arguments = $esxcli2.storage.vmfs.unmap.CreateArgs() - $arguments.volumelabel = $ClusterDataStore - $arguments.reclaimunit = "256" - if ($PSCmdlet.ShouldProcess( $ClusterDataStore,"Starting UNMAP on $myHost")) { - $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() - try { - Write-Output "Starting UNMAP for $ClusterDataStore on $myHost..." - $esxcli2.storage.vmfs.unmap.Invoke($arguments) - } - catch { - Write-Output "A Error occured: " "" $error[0] "" - } - $stopwatch.Stop() - Write-Output "UNMAP duration: $($stopwatch.Elapsed.Minutes)" - } - - } - } - else { - Write-Error "Validation Failed. Processing Loop Skipped!" - } - #endregion - - #region: Revert PowerCLI Session Timeout - Write-Verbose "Revert Session Timeout ..." - Set-PowerCLIConfiguration -Scope Session -WebOperationTimeoutSeconds $initialTimeout -Confirm:$False | Out-Null - #endregion - } - -} diff --git a/Modules/VAMI.psm1 b/Modules/VAMI.psm1 deleted file mode 100755 index 92c5d5f..0000000 --- a/Modules/VAMI.psm1 +++ /dev/null @@ -1,716 +0,0 @@ -Function Get-VAMISummary { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves some basic information from VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return basic VAMI summary info - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMISummary -#> - $systemVersionAPI = Get-CisService -Name 'com.vmware.appliance.system.version' - $results = $systemVersionAPI.get() | select product, type, version, build, install_time - - $systemUptimeAPI = Get-CisService -Name 'com.vmware.appliance.system.uptime' - $ts = [timespan]::fromseconds($systemUptimeAPI.get().toString()) - $uptime = $ts.ToString("hh\:mm\:ss\,fff") - - $summaryResult = [pscustomobject] @{ - Product = $results.product; - Type = $results.type; - Version = $results.version; - Build = $results.build; - InstallTime = $results.install_time; - Uptime = $uptime - } - $summaryResult -} - -Function Get-VAMIHealth { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves health information from VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return VAMI health - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIHealth -#> - $healthOverall = (Get-CisService -Name 'com.vmware.appliance.health.system').get() - $healthLastCheck = (Get-CisService -Name 'com.vmware.appliance.health.system').lastcheck() - $healthCPU = (Get-CisService -Name 'com.vmware.appliance.health.load').get() - $healthMem = (Get-CisService -Name 'com.vmware.appliance.health.mem').get() - $healthSwap = (Get-CisService -Name 'com.vmware.appliance.health.swap').get() - $healthStorage = (Get-CisService -Name 'com.vmware.appliance.health.storage').get() - - # DB health only applicable for Embedded/External VCSA Node - $vami = (Get-CisService -Name 'com.vmware.appliance.system.version').get() - - if($vami.type -eq "vCenter Server with an embedded Platform Services Controller" -or $vami.type -eq "vCenter Server with an external Platform Services Controller") { - $healthVCDB = (Get-CisService -Name 'com.vmware.appliance.health.databasestorage').get() - } else { - $healthVCDB = "N/A" - } - $healthSoftwareUpdates = (Get-CisService -Name 'com.vmware.appliance.health.softwarepackages').get() - - $healthResult = [pscustomobject] @{ - HealthOverall = $healthOverall; - HealthLastCheck = $healthLastCheck; - HealthCPU = $healthCPU; - HealthMem = $healthMem; - HealthSwap = $healthSwap; - HealthStorage = $healthStorage; - HealthVCDB = $healthVCDB; - HealthSoftware = $healthSoftwareUpdates - } - $healthResult -} - -Function Get-VAMIAccess { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves access information from VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return VAMI access interfaces (Console,DCUI,Bash Shell & SSH) - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIAccess -#> - $consoleAccess = (Get-CisService -Name 'com.vmware.appliance.access.consolecli').get() - $dcuiAccess = (Get-CisService -Name 'com.vmware.appliance.access.dcui').get() - $shellAccess = (Get-CisService -Name 'com.vmware.appliance.access.shell').get() - $sshAccess = (Get-CisService -Name 'com.vmware.appliance.access.ssh').get() - - $accessResult = New-Object PSObject -Property @{ - Console = $consoleAccess; - DCUI = $dcuiAccess; - BashShell = $shellAccess.enabled; - SSH = $sshAccess - } - $accessResult -} - -Function Get-VAMITime { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves the time and NTP info from VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return current Time and NTP information - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMITime -#> - $systemTimeAPI = Get-CisService -Name 'com.vmware.appliance.system.time' - $timeResults = $systemTimeAPI.get() - - $timeSync = (Get-CisService -Name 'com.vmware.appliance.techpreview.timesync').get() - $timeSyncMode = $timeSync.mode - - $timeResult = [pscustomobject] @{ - Timezone = $timeResults.timezone; - Date = $timeResults.date; - CurrentTime = $timeResults.time; - Mode = $timeSyncMode; - NTPServers = "N/A"; - NTPStatus = "N/A"; - } - - if($timeSyncMode -eq "NTP") { - $ntpServers = (Get-CisService -Name 'com.vmware.appliance.techpreview.ntp').get() - $timeResult.NTPServers = $ntpServers.servers - $timeResult.NTPStatus = $ntpServers.status - } - $timeResult -} - -Function Get-VAMINetwork { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves network information from VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return networking information including details for each interface - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMINetwork -#> - $netResults = @() - - $Hostname = (Get-CisService -Name 'com.vmware.appliance.networking.dns.hostname').get() - $dns = (Get-CisService -Name 'com.vmware.appliance.networking.dns.servers').get() - - Write-Host "Hostname: " $hostname - Write-Host "DNS Servers: " $dns.servers - - $interfaces = (Get-CisService -Name 'com.vmware.appliance.networking.interfaces').list() - foreach ($interface in $interfaces) { - $ipv4API = (Get-CisService -Name 'com.vmware.appliance.techpreview.networking.ipv4') - $spec = $ipv4API.Help.get.interfaces.CreateExample() - $spec+= $interface.name - $ipv4result = $ipv4API.get($spec) - - $interfaceResult = [pscustomobject] @{ - Inteface = $interface.name; - MAC = $interface.mac; - Status = $interface.status; - Mode = $ipv4result.mode; - IP = $ipv4result.address; - Prefix = $ipv4result.prefix; - Gateway = $ipv4result.default_gateway; - Updateable = $ipv4result.updateable - } - $netResults += $interfaceResult - } - $netResults -} - -Function Get-VAMIDisks { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves VMDK disk number to partition mapping VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return VMDK disk number to OS partition mapping - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIDisks -#> - $storageAPI = Get-CisService -Name 'com.vmware.appliance.system.storage' - $disks = $storageAPI.list() - - foreach ($disk in $disks | sort {[int]$_.disk.toString()}) { - $disk | Select Disk, Partition - } -} - -Function Start-VAMIDiskResize { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function triggers an OS partition resize after adding additional disk capacity - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function triggers OS partition resize operation - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Start-VAMIDiskResize -#> - $storageAPI = Get-CisService -Name 'com.vmware.appliance.system.storage' - Write-Host "Initiated OS partition resize operation ..." - $storageAPI.resize() -} - -Function Get-VAMIStatsList { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves list avialable monitoring metrics in VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return list of available monitoring metrics that can be queried - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIStatsList -#> - $monitoringAPI = Get-CisService -Name 'com.vmware.appliance.monitoring' - $ids = $monitoringAPI.list() | Select id | Sort-Object -Property id - - foreach ($id in $ids) { - $id - } -} - -Function Get-VAMIStorageUsed { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves the individaul OS partition storage utilization - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return individual OS partition storage utilization - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIStorageUsed -#> - $monitoringAPI = Get-CisService 'com.vmware.appliance.monitoring' - $querySpec = $monitoringAPI.help.query.item.CreateExample() - - # List of IDs from Get-VAMIStatsList to query - $querySpec.Names = @( - "storage.used.filesystem.autodeploy", - "storage.used.filesystem.boot", - "storage.used.filesystem.coredump", - "storage.used.filesystem.imagebuilder", - "storage.used.filesystem.invsvc", - "storage.used.filesystem.log", - "storage.used.filesystem.netdump", - "storage.used.filesystem.root", - "storage.used.filesystem.updatemgr", - "storage.used.filesystem.vcdb_core_inventory", - "storage.used.filesystem.vcdb_seat", - "storage.used.filesystem.vcdb_transaction_log", - "storage.totalsize.filesystem.autodeploy", - "storage.totalsize.filesystem.boot", - "storage.totalsize.filesystem.coredump", - "storage.totalsize.filesystem.imagebuilder", - "storage.totalsize.filesystem.invsvc", - "storage.totalsize.filesystem.log", - "storage.totalsize.filesystem.netdump", - "storage.totalsize.filesystem.root", - "storage.totalsize.filesystem.updatemgr", - "storage.totalsize.filesystem.vcdb_core_inventory", - "storage.totalsize.filesystem.vcdb_seat", - "storage.totalsize.filesystem.vcdb_transaction_log" - ) - - # Tuple (Filesystem Name, Used, Total) to store results - $storageStats = @{ - "autodeploy"=@{"name"="/storage/autodeploy";"used"=0;"total"=0}; - "boot"=@{"name"="/boot";"used"=0;"total"=0}; - "coredump"=@{"name"="/storage/core";"used"=0;"total"=0}; - "imagebuilder"=@{"name"="/storage/imagebuilder";"used"=0;"total"=0}; - "invsvc"=@{"name"="/storage/invsvc";"used"=0;"total"=0}; - "log"=@{"name"="/storage/log";"used"=0;"total"=0}; - "netdump"=@{"name"="/storage/netdump";"used"=0;"total"=0}; - "root"=@{"name"="/";"used"=0;"total"=0}; - "updatemgr"=@{"name"="/storage/updatemgr";"used"=0;"total"=0}; - "vcdb_core_inventory"=@{"name"="/storage/db";"used"=0;"total"=0}; - "vcdb_seat"=@{"name"="/storage/seat";"used"=0;"total"=0}; - "vcdb_transaction_log"=@{"name"="/storage/dblog";"used"=0;"total"=0} - } - - $querySpec.interval = "DAY1" - $querySpec.function = "MAX" - $querySpec.start_time = ((get-date).AddDays(-1)) - $querySpec.end_time = (Get-Date) - $queryResults = $monitoringAPI.query($querySpec) | Select * -ExcludeProperty Help - - foreach ($queryResult in $queryResults) { - # Update hash if its used storage results - if($queryResult.name -match "used") { - $key = (($queryResult.name).toString()).split(".")[-1] - $value = [Math]::Round([int]($queryResult.data[1]).toString()/1MB,2) - $storageStats[$key]["used"] = $value - # Update hash if its total storage results - } else { - $key = (($queryResult.name).toString()).split(".")[-1] - $value = [Math]::Round([int]($queryResult.data[1]).toString()/1MB,2) - $storageStats[$key]["total"] = $value - } - } - - $storageResults = @() - foreach ($key in $storageStats.keys | Sort-Object -Property name) { - $statResult = [pscustomobject] @{ - Filesystem = $storageStats[$key].name; - Used = $storageStats[$key].used; - Total = $storageStats[$key].total - } - $storageResults += $statResult - } - $storageResults -} - -Function Get-VAMIService { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves list of services in VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return list of services and their description - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIService - .EXAMPLE - Get-VAMIService -Name rbd -#> - param( - [Parameter( - Mandatory=$false, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true) - ] - [String]$Name - ) - - if($Name -ne "") { - $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' - - try { - $serviceStatus = $vMonAPI.get($name,0) - $serviceString = [pscustomobject] @{ - Name = $name; - State = $serviceStatus.state; - Health = ""; - Startup = $serviceStatus.startup_type - } - if($serviceStatus.health -eq $null) { $serviceString.Health = "N/A"} else { $serviceString.Health = $serviceStatus.health } - $serviceString - } catch { - Write-Error $Error[0].exception.Message - } - } else { - $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' - $services = $vMonAPI.list_details() - - $serviceResult = @() - foreach ($key in $services.keys | Sort-Object -Property Value) { - $serviceString = [pscustomobject] @{ - Name = $key; - State = $services[$key].state; - Health = "N/A"; - Startup = $services[$key].Startup_type - } - if($services[$key].health -eq $null) { $serviceString.Health = "N/A"} else { $serviceString.Health = $services[$key].health } - - $serviceResult += $serviceString - } - $serviceResult - } -} - -Function Start-VAMIService { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves list of services in VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return list of services and their description - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Start-VAMIService -Name rbd -#> - param( - [Parameter( - Mandatory=$true, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true) - ] - [String]$Name - ) - - $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' - - try { - Write-Host "Starting $name service ..." - $vMonAPI.start($name) - } catch { - Write-Error $Error[0].exception.Message - } -} - -Function Stop-VAMIService { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves list of services in VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return list of services and their description - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Stop-VAMIService -Name rbd -#> - param( - [Parameter( - Mandatory=$true, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true) - ] - [String]$Name - ) - - $vMonAPI = Get-CisService 'com.vmware.appliance.vmon.service' - - try { - Write-Host "Stopping $name service ..." - $vMonAPI.stop($name) - } catch { - Write-Error $Error[0].exception.Message - } -} - -Function Get-VAMIBackupSize { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves the backup size of the VCSA from VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to return the current backup size of the VCSA (common and core data) - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIBackupSize -#> - $recoveryAPI = Get-CisService 'com.vmware.appliance.recovery.backup.parts' - $backupParts = $recoveryAPI.list() | select id - - $estimateBackupSize = 0 - $backupPartSizes = "" - foreach ($backupPart in $backupParts) { - $partId = $backupPart.id.value - $partSize = $recoveryAPI.get($partId) - $estimateBackupSize += $partSize - $backupPartSizes += $partId + " data is " + $partSize + " MB`n" - } - - Write-Host "Estimated Backup Size: $estimateBackupSize MB" - Write-Host $backupPartSizes -} - -Function Get-VAMIUser { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves VAMI local users using VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to retrieve VAMI local users - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIUser -#> - param( - [Parameter( - Mandatory=$false, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true) - ] - [String]$Name - ) - - $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' - - $userResults = @() - - if($Name -ne "") { - try { - $user = $userAPI.get($name) - - $userString = [pscustomobject] @{ - User = $user.username - Name = $user.fullname - Email = $user.email - Status = $user.status - PasswordStatus = $user.passwordstatus - Role = $user.role - } - $userResults += $userString - } catch { - Write-Error $Error[0].exception.Message - } - } else { - $users = $userAPI.list() - - foreach ($user in $users) { - $userString = [pscustomobject] @{ - User = $user.username - Name = $user.fullname - Email = $user.email - Status = $user.status - PasswordStatus = $user.passwordstatus - Role = $user.role - } - $userResults += $userString - } - } - $userResults -} - -Function New-VAMIUser { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function to create new VAMI local user using VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to create a new VAMI local user - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - New-VAMIUser -name lamw -fullname "William Lam" -role "operator" -email "lamw@virtuallyghetto.com" -password "VMware1!" -#> - param( - [Parameter( - Mandatory=$true) - ] - [String]$name, - [Parameter( - Mandatory=$true) - ] - [String]$fullname, - [Parameter( - Mandatory=$true) - ] - [ValidateSet("admin","operator","superAdmin")][String]$role, - [Parameter( - Mandatory=$false) - ] - [String]$email="", - [Parameter( - Mandatory=$true) - ] - [String]$password - ) - - $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' - $createSpec = $userAPI.Help.add.config.CreateExample() - - $createSpec.username = $name - $createSpec.fullname = $fullname - $createSpec.role = $role - $createSpec.email = $email - $createSpec.password = [VMware.VimAutomation.Cis.Core.Types.V1.Secret]$password - - try { - Write-Host "Creating new user $name ..." - $userAPI.add($createSpec) - } catch { - Write-Error $Error[0].exception.Message - } -} - -Function Remove-VAMIUser { -<# - .NOTES - =========================================================================== - Created by: William Lam - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function to remove VAMI local user using VAMI interface (5480) - for a VCSA node which can be an Embedded VCSA, External PSC or External VCSA. - .DESCRIPTION - Function to remove VAMI local user - .EXAMPLE - Connect-CisServer -Server 192.168.1.51 -User administrator@vsphere.local -Password VMware1! - Get-VAMIAccess -#> - param( - [Parameter( - Mandatory=$true) - ] - [String]$name, - [Parameter( - Mandatory=$false) - ] - [boolean]$confirm=$false - ) - - if(!$confirm) { - $answer = Read-Host -Prompt "Do you want to delete user $name (Y or N)" - if($answer -eq "Y" -or $answer -eq "y") { - $userAPI = Get-CisService 'com.vmware.appliance.techpreview.localaccounts.user' - - try { - Write-Host "Deleting user $name ..." - $userAPI.delete($name) - } catch { - Write-Error $Error[0].exception.Message - } - } - } -} \ No newline at end of file diff --git a/Modules/VCHA.psm1 b/Modules/VCHA.psm1 deleted file mode 100644 index 160f0e7..0000000 --- a/Modules/VCHA.psm1 +++ /dev/null @@ -1,413 +0,0 @@ -Function Get-VCHAConfig { -<# - .NOTES - =========================================================================== - Created by: William Lam - Date: Nov 20, 2016 - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves the VCHA Configuration which provides you with - the current state, mode as well as the IP Addresses of the Active, - Passive & Witness Node. This is only available on VCSA 6.5 (vSphere 6.5 or greater) - .DESCRIPTION - Function to return VCHA Configuration - .EXAMPLE - Get-VCHAConfig -#> - $vcHAClusterConfig = Get-View failoverClusterConfigurator - $vcHAConfig = $vcHAClusterConfig.getVchaConfig() - - $vcHAState = $vcHAConfig.State - switch($vcHAState) { - configured { - $activeIp = $vcHAConfig.FailoverNodeInfo1.ClusterIpSettings.Ip.IpAddress - $passiveIp = $vcHAConfig.FailoverNodeInfo2.ClusterIpSettings.Ip.IpAddress - $witnessIp = $vcHAConfig.WitnessNodeInfo.IpSettings.Ip.IpAddress - - $vcHAClusterManager = Get-View failoverClusterManager - $vcHAMode = $vcHAClusterManager.getClusterMode() - - Write-Host "" - Write-Host -NoNewline -ForegroundColor Green "VCHA State: " - Write-Host -ForegroundColor White "$vcHAState" - Write-Host -NoNewline -ForegroundColor Green " VCHA Mode: " - Write-Host -ForegroundColor White "$vcHAMode" - Write-Host -NoNewline -ForegroundColor Green " ActiveIP: " - Write-Host -ForegroundColor White "$activeIp" - Write-Host -NoNewline -ForegroundColor Green " PassiveIP: " - Write-Host -ForegroundColor White "$passiveIp" - Write-Host -NoNewline -ForegroundColor Green " WitnessIP: " - Write-Host -ForegroundColor White "$witnessIp`n" - ;break - } - invalid { Write-Host -ForegroundColor Red "VCHA State is in invalid state ...";break} - notConfigured { Write-Host "VCHA is not configured";break} - prepared { Write-Host "VCHA is being prepared, please try again in a little bit ...";break} - } -} - -Function Get-VCHAClusterHealth { -<# - .NOTES - =========================================================================== - Created by: William Lam - Date: Nov 20, 2016 - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function retrieves the VCHA Cluster Health which provides more info - on each of the individual. This is only available on VCSA 6.5 (vSphere 6.5 or greater) - .DESCRIPTION - Function to return VCHA Cluster Health - .EXAMPLE - Get-VCHAClusterHealth -#> - $vcHAClusterConfig = Get-View failoverClusterConfigurator - $vcHAConfig = $vcHAClusterConfig.getVchaConfig() - $vcHAState = $vcHAConfig.State - - switch($vcHAState) { - invalid { Write-Host -ForegroundColor Red "VCHA State is in invalid state ...";break} - notConfigured { Write-Host "VCHA is not configured";break} - prepared { Write-Host "VCHA is being prepared ...";break} - configured { - $vcHAClusterManager = Get-View failoverClusterManager - $healthInfo = $vcHAClusterManager.GetVchaClusterHealth() - - $vcClusterState = $healthInfo.RuntimeInfo.ClusterState - $nodeState = $healthInfo.RuntimeInfo.NodeInfo - - Write-Host "" - Write-Host -NoNewline -ForegroundColor Green "VCHA Cluster State: " - Write-Host -ForegroundColor White "$vcClusterState" - Write-Host -NoNewline -ForegroundColor Green "VCHA Node Information: " - $nodeState | Select NodeIp, NodeRole, NodeState - ;break - } - } -} - -Function Set-VCHAClusterMode { -<# - .NOTES - =========================================================================== - Created by: William Lam - Date: Nov 20, 2016 - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function allows you to set the mode of the VCHA Cluster whether - that is Enabled, Disabled or in Maintenance Mode. This is only available on VCSA 6.5 (vSphere 6.5 or greater) - .DESCRIPTION - Function to set VCHA Cluster Mode - .EXAMPLE - Set-VCHAClusterMode -Enabled $true - .EXAMPLE - Set-VCHAClusterMode -Disabled $true - .EXAMPLE - Set-VCHAClusterMode -Maintenance $true -#> - param( - [Switch]$Enabled, - [Switch]$Disabled, - [Switch]$Maintenance - ) - - $vcHAClusterManager = Get-View failoverClusterManager - - if($Enabled) { - Write-Host "Setting VCHA Cluster to Enabled ..." - $task = $vcHAClusterManager.setClusterMode_Task("enabled") - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task - } elseIf($Maintenance) { - Write-Host "Setting VCHA Cluster to Maintenance ..." - $task = $vcHAClusterManager.setClusterMode_Task("maintenance") - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task - } elseIf($Disabled) { - Write-Host "`nSetting VCHA Cluster to Disabled ...`n" - $task = $vcHAClusterManager.setClusterMode_Task("disabled") - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task - } -} - -Function New-VCHABasicConfig { -<# - .NOTES - =========================================================================== - Created by: William Lam - Date: Nov 20, 2016 - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function allows you create a new "Basic" VCHA Cluster, it does not - cover the "Advanced" use case. You will need to ensure that you have a - "Self Managed" vCenter Server before attempting this workflow. - This is only available on VCSA 6.5 (vSphere 6.5 or greater) - .DESCRIPTION - Function to create "Basic" VCHA Cluster - .PARAMETER VCSAVM - The name of the vCenter Server Appliance (VCSA) in which you wish to enable VCHA on (must be self-managed) - .PARAMETER HANetwork - The name of the Virtual Portgroup or Distributed Portgroup used for the HA Network - .PARAMETER ActiveHAIp - The IP Address for the Active VCSA node - .PARAMETER ActiveNetmask - The Netmask for the Active VCSA node - .PARAMETER PassiveHAIp - The IP Address for the Passive VCSA node - .PARAMETER PassiveNetmask - The Netmask for the Passive VCSA node - .PARAMETER WitnessHAIp - The IP Address for the Witness VCSA node - .PARAMETER WitnessNetmask - The Netmask for the Witness VCSA node - .PARAMETER PassiveDatastore - The name of the datastore to deploy the Passive node to - .PARAMETER WitnessDatastore - The name of the datastore to deploy the Witness node to - .PARAMETER VCUsername - The VCSA username (e.g. administrator@vghetto.local) - .PARAMETER VCPassword - The VCSA password - .EXAMPLE - New-VCHABasicConfig -VCSAVM "vcenter65-1" -HANetwork "DVPG-VCHA-Network" ` - -ActiveHAIp 192.168.1.70 ` - -ActiveNetmask 255.255.255.0 ` - -PassiveHAIp 192.168.1.71 ` - -PassiveNetmask 255.255.255.0 ` - -WitnessHAIp 192.168.1.72 ` - -WitnessNetmask 255.255.255.0 ` - -PassiveDatastore "vsanDatastore" ` - -WitnessDatastore "vsanDatastore" ` - -VCUsername "administrator@vghetto.local" ` - -VCPassword "VMware1!" -#> - param( - [Parameter( - Mandatory=$true, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true) - ] - [String]$VCSAVM, - [String]$HANetwork, - [String]$ActiveHAIp, - [String]$ActiveNetmask, - [String]$PassiveHAIp, - [String]$PassiveNetmask, - [String]$PassiveDatastore, - [String]$WitnessHAIp, - [String]$WitnessNetmask, - [String]$WitnessDatastore, - # Crappy Implementation but need to research more into using PSH Credential - [String]$VCUsername, - [String]$VCPassword - ) - - $VCSAVMView = Get-View -ViewType VirtualMachine -Filter @{"name"=$VCSAVM} - if($VCSAVMView -eq $null) { - Write-Host -ForegroundColor Red "Error: Unable to find Virtual Machine $VCSAVM" - return - } - - $HANetworkView = Get-View -ViewType Network -Filter @{"name"=$HANetwork} - if($HANetworkView -eq $null) { - Write-Host -ForegroundColor Red "Error: Unable to find Network $HANetwork" - return - } - - $PassiveDatastoreView = Get-View -ViewType Datastore -Filter @{"name"=$PassiveDatastore} - if($PassiveDatastoreView -eq $null) { - Write-Host -ForegroundColor Red "Error: Unable to find Passive Datastore $PassiveDatastore" - return - } - - $WitnessDatastoreView = Get-View -ViewType Datastore -Filter @{"name"=$WitnessDatastore} - if($WitnessDatastoreView -eq $null) { - Write-Host -ForegroundColor Red "Error: Unable to find Witness Datastore $WitnessDatastore" - return - } - - $vcIP = $VCSAVMView.Guest.IpAddress - if($vcIP -eq $null) { - Write-Host -ForegroundColor Red "Error: Unable to automatically retrieve the IP Address of $VCSAVM which is needed to use this function" - return - } - - # Retrieve Source VC SSL Thumbprint - $vcurl = "https://$vcIP" -add-type @" - using System.Net; - using System.Security.Cryptography.X509Certificates; - - public class IDontCarePolicy : ICertificatePolicy { - public IDontCarePolicy() {} - public bool CheckValidationResult( - ServicePoint sPoint, X509Certificate cert, - WebRequest wRequest, int certProb) { - return true; - } - } -"@ - [System.Net.ServicePointManager]::CertificatePolicy = new-object IDontCarePolicy - # Need to do simple GET connection for this method to work - Invoke-RestMethod -Uri $VCURL -Method Get | Out-Null - - $endpoint_request = [System.Net.Webrequest]::Create("$vcurl") - # Get Thumbprint + add colons for a valid Thumbprint - $vcSSLThumbprint = ($endpoint_request.ServicePoint.Certificate.GetCertHashString()) -replace '(..(?!$))','$1:' - - $vcHAClusterConfig = Get-View failoverClusterConfigurator - $spec = New-Object VMware.Vim.VchaClusterDeploymentSpec - - $activeNetworkConfig = New-Object VMware.Vim.ClusterNetworkConfigSpec - $activeNetworkConfig.NetworkPortGroup = $HANetworkView.MoRef - $ipSettings = New-Object Vmware.Vim.CustomizationIPSettings - $ipSettings.SubnetMask = $ActiveNetmask - $activeIpSpec = New-Object VMware.Vim.CustomizationFixedIp - $activeIpSpec.IpAddress = $ActiveHAIp - $ipSettings.Ip = $activeIpSpec - $activeNetworkConfig.IpSettings = $ipSettings - $spec.ActiveVcNetworkConfig = $activeNetworkConfig - - $activeVCConfig = New-Object Vmware.Vim.SourceNodeSpec - $activeVCConfig.ActiveVc = $VCSAVMView.MoRef - $serviceLocator = New-Object Vmware.Vim.ServiceLocator - $credential = New-Object VMware.Vim.ServiceLocatorNamePassword - $credential.username = $VCUsername - $credential.password = $VCPassword - $serviceLocator.Credential = $credential - $serviceLocator.InstanceUuid = $global:DefaultVIServer.InstanceUuid - $serviceLocator.Url = $vcurl - $serviceLocator.SslThumbprint = $vcSSLThumbprint - $activeVCConfig.ManagementVc = $serviceLocator - $spec.ActiveVcSpec = $activeVCConfig - - $passiveSpec = New-Object VMware.Vim.PassiveNodeDeploymentSpec - $passiveSpec.Folder = (Get-View (Get-Folder vm)).MoRef - $passiveIpSettings = New-object Vmware.Vim.CustomizationIPSettings - $passiveIpSettings.SubnetMask = $passiveNetmask - $passiveIpSpec = New-Object VMware.Vim.CustomizationFixedIp - $passiveIpSpec.IpAddress = $passiveHAIp - $passiveIpSettings.Ip = $passiveIpSpec - $passiveSpec.IpSettings = $passiveIpSettings - $passiveSpec.NodeName = $VCSAVMView.Name + "-Passive" - $passiveSpec.datastore = $PassiveDatastoreView.MoRef - $spec.PassiveDeploymentSpec = $passiveSpec - - $witnessSpec = New-Object VMware.Vim.NodeDeploymentSpec - $witnessSpec.Folder = (Get-View (Get-Folder vm)).MoRef - $witnessSpec.NodeName = $VCSAVMView.Name + "-Witness" - $witnessIpSettings = New-object Vmware.Vim.CustomizationIPSettings - $witnessIpSettings.SubnetMask = $witnessNetmask - $witnessIpSpec = New-Object VMware.Vim.CustomizationFixedIp - $witnessIpSpec.IpAddress = $witnessHAIp - $witnessIpSettings.Ip = $witnessIpSpec - $witnessSpec.IpSettings = $witnessIpSettings - $witnessSpec.datastore = $WitnessDatastoreView.MoRef - $spec.WitnessDeploymentSpec = $witnessSpec - - Write-Host "`nDeploying VCHA Cluster ...`n" - $task = $vcHAClusterConfig.deployVcha_Task($spec) - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task -Verbose -} - -Function Remove-VCHAConfig { -<# - .NOTES - =========================================================================== - Created by: William Lam - Date: Nov 20, 2016 - Organization: VMware - Blog: www.virtuallyghetto.com - Twitter: @lamw - =========================================================================== - .SYNOPSIS - This function allows you destroy a VCHA Cluster. In addition, you have - the option to specify whether you would like both the Passive & Witness - Virtual Machines be deleted after the VCHA Cluster has been destroyed. - This is only available on VCSA 6.5 (vSphere 6.5 or greater) - .DESCRIPTION - Function to destroy a VCHA Cluster Mode - .EXAMPLE - Remove-VCHAConfig - .EXAMPLE - Remove-VCHAConfig -Confirm:$false - .EXAMPLE - Remove-VCHAConfig -DeleteVM $true -Confirm:$false - .NOTES - Before you can destroy a VCHA Cluster, you must make sure it is first - disabled. Run the Set-VCHAClusterMode -Disabled $true to do so -#> - param( - [Boolean]$Confirm=$true, - [Switch]$DeleteVM=$false - ) - - $Verified = $false - if($Confirm -eq $true) { - Write-Host -ForegroundColor Yellow "`nDo you want to destroy VCHA Cluster?" - $answer = Read-Host -Prompt "Do you accept (Y or N)" - if($answer -eq "Y" -or $answer -eq "y") { - $Verified = $true - } - } else { - $Verified = $true - } - - if($Verified) { - $vcHAClusterManager = Get-View failoverClusterManager - $vcHAMode = $vcHAClusterManager.getClusterMode() - - if($vcHAMode -ne "disabled") { - Write-Host -ForegroundColor Yellow "To destroy VCHA Cluster, you must first set the VCHA Cluster Mode to `"Disabled`"" - Exit - } - - # Query BIOS UUID of the Passive/Witness to be able to delete - if($DeleteVM) { - $vcHAClusterConfig = Get-View failoverClusterConfigurator - $vcHAConfig = $vcHAClusterConfig.getVchaConfig() - $passiveBiosUUID = $vcHAConfig.FailoverNodeInfo2.biosUuid - $witnessBiosUUID = $vcHAConfig.WitnessNodeInfo.biosUuid - } - - $vcHAClusterConfig = Get-View failoverClusterConfigurator - - Write-Host "Destroying VCHA Cluster ..." - $task = $vcHAClusterConfig.destroyVcha_Task() - $task1 = Get-Task -Id ("Task-$($task.value)") - $task1 | Wait-Task - - # After VCHA Cluster has been destroyed, we can now delete the VMs we had queried earlier - if($DeleteVM) { - if($passiveBiosUUID -ne $null -and $witnessBiosUUID -ne $null) { - $searchIndex = Get-View searchIndex - - $passiveVM = $searchIndex.FindByUuid($null,$passiveBiosUUID,$true,$null) - $witnessVM = $searchIndex.FindByUuid($null,$witnessBiosUUID,$true,$null) - - if($passiveVM -ne $null -and $witnessVM -ne $null) { - Write-Host "Powering off & deleting Passive VM ..." - Stop-VM -VM (Get-View $passiveVM).Name -Confirm:$false | Out-Null - Remove-VM (Get-View $passiveVM).Name -DeletePermanently -Confirm:$false - Write-Host "Powering off & deleting Witness VM ..." - Stop-VM -VM (Get-View $witnessVM).Name -Confirm:$false | Out-Null - Remove-VM (Get-View $witnessVM).Name -DeletePermanently -Confirm:$false - } - } - } - } -} diff --git a/Modules/VMCPFunctions.psm1 b/Modules/VMCPFunctions.psm1 deleted file mode 100644 index 4f9b16e..0000000 --- a/Modules/VMCPFunctions.psm1 +++ /dev/null @@ -1,322 +0,0 @@ -function Get-VMCPSettings { -<# - .NOTES - =========================================================================== - Created on: 10/27/2015 9:25 PM - Created by: Brian Graf - Twitter: @vBrianGraf - VMware Blog: blogs.vmware.com/powercli - Personal Blog: www.vtagion.com - - Modified on: 10/11/2016 - Modified by: Erwan Quélin - Twitter: @erwanquelin - Github: https://github.com/equelin - =========================================================================== - .DESCRIPTION - This function will allow users to view the VMCP settings for their clusters - - .PARAMETER Cluster - Cluster Name or Object - - .PARAMETER Server - vCenter server object - - .EXAMPLE - Get-VMCPSettings - - This will show you the VMCP settings for all the clusters - - .EXAMPLE - Get-VMCPSettings -cluster LAB-CL - - This will show you the VMCP settings of your cluster - - .EXAMPLE - Get-VMCPSettings -cluster (Get-Cluster Lab-CL) - - This will show you the VMCP settings of your cluster - - .EXAMPLE - Get-Cluster | Get-VMCPSettings - - This will show you the VMCP settings for all the clusters -#> - [CmdletBinding()] - param - ( - [Parameter(Mandatory=$False, - ValueFromPipeline=$True, - ValueFromPipelineByPropertyName=$True, - HelpMessage='What is the Cluster Name?')] - $cluster = (Get-Cluster -Server $Server), - - [Parameter(Mandatory=$False)] - [VMware.VimAutomation.Types.VIServer[]]$Server = $global:DefaultVIServers - ) - - Process { - - Foreach ($Clus in $Cluster) { - - Write-Verbose "Processing Cluster $($Clus.Name)" - - # Determine input and convert to ClusterImpl object - Switch ($Clus.GetType().Name) - { - "string" {$CL = Get-Cluster $Clus -Server $Server -ErrorAction SilentlyContinue} - "ClusterImpl" {$CL = $Clus} - } - - If ($CL) { - # Work with the Cluster View - $ClusterMod = Get-View -Id "ClusterComputeResource-$($CL.ExtensionData.MoRef.Value)" -Server $Server - - # Create Hashtable with desired properties to return - $properties = [ordered]@{ - 'Cluster' = $ClusterMod.Name; - 'VMCP Status' = $clustermod.Configuration.DasConfig.VmComponentProtecting; - 'Protection For APD' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmStorageProtectionForAPD; - 'APD Timeout Enabled' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.EnableAPDTimeoutForHosts; - 'APD Timeout (Seconds)' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmTerminateDelayForAPDSec; - 'Reaction on APD Cleared' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmReactionOnAPDCleared; - 'Protection For PDL' = $clustermod.Configuration.DasConfig.DefaultVmSettings.VmComponentProtectionSettings.VmStorageProtectionForPDL - } - - # Create PSObject with the Hashtable - $object = New-Object -TypeName PSObject -Prop $properties - - # Show object - $object - } - } - } -} - -function Set-VMCPSettings { -<# - .NOTES - =========================================================================== - Created on: 10/27/2015 9:25 PM - Created by: Brian Graf - Twitter: @vBrianGraf - VMware Blog: blogs.vmware.com/powercli - Personal Blog: www.vtagion.com - - Modified on: 10/11/2016 - Modified by: Erwan Quélin - Twitter: @erwanquelin - Github: https://github.com/equelin - =========================================================================== - .DESCRIPTION - This function will allow users to enable/disable VMCP and also allow - them to configure the additional VMCP settings - For each parameter, users should use the 'Tab' button to auto-fill the - possible values. - - .PARAMETER Cluster - Cluster Name or Object - - .PARAMETER enableVMCP - Enable or disable VMCP - - .PARAMETER VmStorageProtectionForPDL - VM Storage Protection for PDL settings. Might be: - - disabled - - warning - - restartAggressive - - .PARAMETER VmStorageProtectionForAPD - VM Storage Protection for APD settings. Might be: - - disabled - - restartConservative - - restartAggressive - - warning - - .PARAMETER VmTerminateDelayForAPDSec - VM Terminate Delay for APD (seconds). - - .PARAMETER VmReactionOnAPDCleared - VM reaction on APD Cleared. Might be: - - reset - - none - - .PARAMETER Server - vCenter server object - - .EXAMPLE - Set-VMCPSettings -cluster LAB-CL -enableVMCP:$True -VmStorageProtectionForPDL ` - restartAggressive -VmStorageProtectionForAPD restartAggressive ` - -VmTerminateDelayForAPDSec 2000 -VmReactionOnAPDCleared reset - - This will enable VMCP and configure the Settings on cluster LAB-CL - - .EXAMPLE - Set-VMCPSettings -cluster LAB-CL -enableVMCP:$False -VmStorageProtectionForPDL ` - disabled -VmStorageProtectionForAPD disabled ` - -VmTerminateDelayForAPDSec 600 -VmReactionOnAPDCleared none - - This will disable VMCP and configure the Settings on cluster LAB-CL - - .EXAMPLE - Set-VMCPSettings -enableVMCP:$False -VmStorageProtectionForPDL ` - disabled -VmStorageProtectionForAPD disabled ` - -VmTerminateDelayForAPDSec 600 -VmReactionOnAPDCleared none - - This will disable VMCP and configure the Settings on all clusters available -#> - [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="High")] - param - ( - [Parameter(Mandatory=$true, - ValueFromPipeline=$True, - ValueFromPipelineByPropertyName=$True, - HelpMessage='What is the Cluster Name?')] - $cluster, - - [Parameter(Mandatory=$False, - ValueFromPipeline=$False, - HelpMessage='$True=Enabled $False=Disabled')] - [bool]$enableVMCP, - - [Parameter(Mandatory=$False, - ValueFromPipeline=$False, - HelpMessage='Actions that can be taken in response to a PDL event')] - [ValidateSet("disabled","warning","restartAggressive")] - [string]$VmStorageProtectionForPDL, - - [Parameter(Mandatory=$False, - ValueFromPipeline=$False, - HelpMessage='Options available for an APD response')] - [ValidateSet("disabled","restartConservative","restartAggressive","warning")] - [string]$VmStorageProtectionForAPD, - - [Parameter(Mandatory=$False, - ValueFromPipeline=$False, - HelpMessage='Value in seconds')] - [Int]$VmTerminateDelayForAPDSec, - - [Parameter(Mandatory=$False, - ValueFromPipeline=$False, - HelpMessage='This setting will instruct vSphere HA to take a certain action if an APD event is cleared')] - [ValidateSet("reset","none")] - [string]$VmReactionOnAPDCleared, - - [Parameter(Mandatory=$False)] - [VMware.VimAutomation.Types.VIServer[]]$Server = $global:DefaultVIServers - ) - - Process { - - Foreach ($Clus in $Cluster) { - - Write-Verbose "Processing Cluster $Clus" - - # Determine input and convert to ClusterImpl object - Switch ($Clus.GetType().Name) - { - "string" {$CL = Get-Cluster $Clus -Server $Server -ErrorAction SilentlyContinue} - "ClusterImpl" {$CL = $Clus} - default {Throw 'Please provide a cluster name or object'} - } - - If ($CL) { - - # Get the actual configuration of the Cluster - $ActualSettings = Get-VMCPSettings -Cluster $CL -Server $Server - - # Show actual settings in the verbose mode - Write-Verbose "[$($CL.Name)] Actual VMCP settings " - Write-Verbose $ActualSettings - - # Create the object we will configure - $settings = New-Object VMware.Vim.ClusterConfigSpecEx - $settings.dasConfig = New-Object VMware.Vim.ClusterDasConfigInfo - - # Based on $enableVMCP switch - if ($enableVMCP -eq $false) { - $settings.dasConfig.vmComponentProtecting = "disabled" - } - elseif ($enableVMCP -eq $true) { - $settings.dasConfig.vmComponentProtecting = "enabled" - } - - #Create the VMCP object to work with - $settings.dasConfig.defaultVmSettings = New-Object VMware.Vim.ClusterDasVmSettings - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings = New-Object VMware.Vim.ClusterVmComponentProtectionSettings - - #Storage Protection For PDL - If ($PSBoundParameters.ContainsKey('VmStorageProtectionForPDL')) { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForPDL = $VmStorageProtectionForPDL - } else { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForPDL = $ActualSettings.'Protection For PDL' - } - - #Storage Protection for APD - If ($PSBoundParameters.ContainsKey('VmStorageProtectionForAPD')) { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = $VmStorageProtectionForAPD - } else { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = $ActualSettings.'Protection For APD' - } - - #Storage Protection for APD - If ($PSBoundParameters.ContainsKey('VmStorageProtectionForAPD')) { - switch ($VmStorageProtectionForAPD) { - "disabled" { - # If Disabled, there is no need to set enable Timeout Value - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'disabled' - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $false - } - - "restartConservative" { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'restartConservative' - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $true - } - - "restartAggressive" { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'restartAggressive' - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $true - } - - "warning" { - # If Warning, there is no need to enable the Timeout Value - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = 'warning' - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $false - } - } - } else { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmStorageProtectionForAPD = $ActualSettings.'Protection For APD' - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.enableAPDTimeoutForHosts = $ActualSettings.'APD Timeout Enabled' - } - - #APD Timeout Enabled - If ($PSBoundParameters.ContainsKey('VmTerminateDelayForAPDSec')) { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmTerminateDelayForAPDSec = $VmTerminateDelayForAPDSec - } else { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmTerminateDelayForAPDSec = $ActualSettings.'APD Timeout (Seconds)' - } - - # Reaction On APD Cleared - If ($PSBoundParameters.ContainsKey('VmReactionOnAPDCleared')) { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmReactionOnAPDCleared = "$VmReactionOnAPDCleared" - } else { - $settings.dasConfig.defaultVmSettings.vmComponentProtectionSettings.vmReactionOnAPDCleared = $ActualSettings.'Reaction on APD Cleared' - } - - # Execute API Call - If ($pscmdlet.ShouldProcess($CL.Name,"Modify VMCP configuration")) { - $modify = $true - $ClusterMod = Get-View -Id "ClusterComputeResource-$($CL.ExtensionData.MoRef.Value)" -Server $Server - $Task = $ClusterMod.ReconfigureComputeResource_Task($settings, $modify) - } - - # Wait for the reconfiguration task to finish to show the result - If ($Task) { - $TaskID = "Task-" + $($Task.Value) - Get-Task -Id $TaskID -Server $Server | Wait-Task | Out-Null - Get-VMCPSettings -Cluster $CL -Server $Server - } - } - } - } -} diff --git a/Modules/apply-hardening.psm1 b/Modules/apply-hardening.psm1 deleted file mode 100644 index 94b1279..0000000 --- a/Modules/apply-hardening.psm1 +++ /dev/null @@ -1,93 +0,0 @@ -function Apply-Hardening { -<# - .NOTES - =========================================================================== - Created by: Markus Kraus - Twitter: @VMarkus_K - Private Blog: mycloudrevolution.com - =========================================================================== - Changelog: - 2016.11 ver 2.0 Base Release - =========================================================================== - External Code Sources: - - =========================================================================== - Tested Against Environment: - vSphere Version: 5.5 U2 - PowerCLI Version: PowerCLI 6.3 R1, PowerCLI 6.5 R1 - PowerShell Version: 4.0, 5.0 - OS Version: Windows 8.1, Server 2012 R2 - Keyword: VM, Hardening, Security - =========================================================================== - - .DESCRIPTION - Applys a set of Hardening options to your VMs - - .Example - Get-VM TST* | Apply-Hardening - - .Example - $SampleVMs = Get-VM "TST*" - Apply-Hardening -VMs $SampleVMs - - .PARAMETER VMs - Specify the VMs - - -#Requires PS -Version 4.0 -#Requires -Modules VMware.VimAutomation.Core, @{ModuleName="VMware.VimAutomation.Core";ModuleVersion="6.3.0.0"} -#> - -[CmdletBinding()] -param( - [Parameter(Mandatory=$true, - ValueFromPipeline=$True, - Position=0)] - [VMware.VimAutomation.ViCore.Impl.V1.Inventory.InventoryItemImpl[]] - $VMs -) - -Process { -#region: Create Options - $ExtraOptions = @{ - "isolation.tools.diskShrink.disable"="true"; - "isolation.tools.diskWiper.disable"="true"; - "isolation.tools.copy.disable"="true"; - "isolation.tools.paste.disable"="true"; - "isolation.tools.dnd.disable"="true"; - "isolation.tools.setGUIOptions.enable"="false"; - "log.keepOld"="10"; - "log.rotateSize"="100000" - "RemoteDisplay.maxConnections"="2"; - "RemoteDisplay.vnc.enabled"="false"; - - } - if ($DebugPreference -eq "Inquire") { - Write-Output "VM Hardening Options:" - $ExtraOptions | Format-Table -AutoSize - } - - $VMConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec - - Foreach ($Option in $ExtraOptions.GetEnumerator()) { - $OptionValue = New-Object VMware.Vim.optionvalue - $OptionValue.Key = $Option.Key - $OptionValue.Value = $Option.Value - $VMConfigSpec.extraconfig += $OptionValue - } -#endregion - -#region: Apply Options - ForEach ($VM in $VMs){ - $VMv = Get-VM $VM | Get-View - $state = $VMv.Summary.Runtime.PowerState - Write-Output "...Starting Reconfiguring VM: $VM " - $TaskConf = ($VMv).ReconfigVM_Task($VMConfigSpec) - if ($state -eq "poweredOn") { - Write-Output "...Migrating VM: $VM " - $TaskMig = $VMv.MigrateVM_Task($null, $_.Runtime.Host, 'highPriority', $null) - } - } - } -#endregion -} \ No newline at end of file diff --git a/Modules/vSphere_Hardening_Assess_VM_v1a.psm1 b/Modules/vSphere_Hardening_Assess_VM_v1a.psm1 deleted file mode 100644 index ad6227c..0000000 --- a/Modules/vSphere_Hardening_Assess_VM_v1a.psm1 +++ /dev/null @@ -1,372 +0,0 @@ -<# - .NOTES - =========================================================================== - Created on: 5/27/2015 3:24 PM - Created by: Brian Graf - Twitter: @vBrianGraf - Blog: http://www.vtagion.com - =========================================================================== -#> - -#Encoded Hardening Guide -$Global:Base64 = "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" - -Function Import-Xls { - -<# -.SYNOPSIS -Import an Excel file. - -.DESCRIPTION -Import an excel file. Since Excel files can have multiple worksheets, you can specify the worksheet you want to import. You can specify it by number (1, 2, 3) or by name (Sheet1, Sheet2, Sheet3). Imports Worksheet 1 by default. - -.PARAMETER Path -Specifies the path to the Excel file to import. You can also pipe a path to Import-Xls. - -.PARAMETER Worksheet -Specifies the worksheet to import in the Excel file. You can specify it by name or by number. The default is 1. -Note: Charts don't count as worksheets, so they don't affect the Worksheet numbers. - -.INPUTS -System.String - -.OUTPUTS -Object - -.EXAMPLE -".\employees.xlsx" | Import-Xls -Worksheet 1 -Import Worksheet 1 from employees.xlsx - -.EXAMPLE -".\employees.xlsx" | Import-Xls -Worksheet "Sheet2" -Import Worksheet "Sheet2" from employees.xlsx - -.EXAMPLE -".\deptA.xslx", ".\deptB.xlsx" | Import-Xls -Worksheet 3 -Import Worksheet 3 from deptA.xlsx and deptB.xlsx. -Make sure that the worksheets have the same headers, or have some headers in common, or that it works the way you expect. - -.EXAMPLE -Get-ChildItem *.xlsx | Import-Xls -Worksheet "Employees" -Import Worksheet "Employees" from all .xlsx files in the current directory. -Make sure that the worksheets have the same headers, or have some headers in common, or that it works the way you expect. - -.LINK -Import-Xls -http://gallery.technet.microsoft.com/scriptcenter/17bcabe7-322a-43d3-9a27-f3f96618c74b -Export-Xls -http://gallery.technet.microsoft.com/scriptcenter/d41565f1-37ef-43cb-9462-a08cd5a610e2 -Import-Csv -Export-Csv - -.NOTES -Author: Francis de la Cerna -Created: 2011-03-27 -Modified: 2011-04-09 -#Requires –Version 2.0 - -#> - - [CmdletBinding(SupportsShouldProcess = $true)] - Param ( - [parameter( - mandatory = $true, - position = 1, - ValueFromPipeline = $true, - ValueFromPipelineByPropertyName = $true)] - [String[]]$Path, - [parameter(mandatory = $false)] - $Worksheet = 1, - [parameter(mandatory = $false)] - [switch]$Force - ) - - Begin { - function GetTempFileName($extension) { - $temp = [io.path]::GetTempFileName(); - $params = @{ - Path = $temp; - Destination = $temp + $extension; - Confirm = $false; - Verbose = $VerbosePreference; - } - Move-Item @params; - $temp += $extension; - return $temp; - } - - # since an extension like .xls can have multiple formats, this - # will need to be changed - # - $xlFileFormats = @{ - # single worksheet formats - '.csv' = 6; # 6, 22, 23, 24 - '.dbf' = 11; # 7, 8, 11 - '.dif' = 9; # - '.prn' = 36; # - '.slk' = 2; # 2, 10 - '.wk1' = 31; # 5, 30, 31 - '.wk3' = 32; # 15, 32 - '.wk4' = 38; # - '.wks' = 4; # - '.xlw' = 35; # - - # multiple worksheet formats - '.xls' = -4143; # -4143, 1, 16, 18, 29, 33, 39, 43 - '.xlsb' = 50; # - '.xlsm' = 52; # - '.xlsx' = 51; # - '.xml' = 46; # - '.ods' = 60; # - } - - $xl = New-Object -ComObject Excel.Application; - $xl.DisplayAlerts = $false; - $xl.Visible = $false; - } - - Process { - $Path | ForEach-Object { - - if ($Force -or $psCmdlet.ShouldProcess($_)) { - - $fileExist = Test-Path $_ - - if (-not $fileExist) { - Write-Error "Error: $_ does not exist" -Category ResourceUnavailable; - } else { - # create temporary .csv file from excel file and import .csv - # - $_ = (Resolve-Path $_).toString(); - $wb = $xl.Workbooks.Add($_); - if ($?) { - $csvTemp = GetTempFileName(".csv"); - $ws = $wb.Worksheets.Item($Worksheet); - $ws.SaveAs($csvTemp, $xlFileFormats[".csv"]); - $wb.Close($false); - Remove-Variable -Name ('ws', 'wb') -Confirm:$false; - Import-Csv $csvTemp; - Remove-Item $csvTemp -Confirm:$false -Verbose:$VerbosePreference; - } - } - } - } - } - - End { - $xl.Quit(); - Remove-Variable -name xl -Confirm:$false; - [gc]::Collect(); - } -} -Function Show-HardeningGuide { - $Global:XLS = Import-XLS $env:temp\HG.XLSX - $ShowGuide = $XLS | Out-GridView -} -Function Assess-VM { -<# - .DESCRIPTION - This cmdlet will allow you to assess the security of the VM's in your environment - .EXAMPLE - # Specify Virtual Machines to Assess - $VM = Get-VM MGMT-AD, MGMT-VCO, WebCommander, Exchange01 - - # Assess against Risk Profile 2 - Assess-VM -VM $VM -RiskProfile 2 - - .EXAMPLE - # Assess all VM's against specific ID's - $VM = Get-VM - Assess-VM -VM $VM -ID 9,10,12,22,33,49 - - .EXAMPLE - # Assess All VM's against All ID's - Assess-VM -VM (Get-VM) -#> - [CmdletBinding( - DefaultParameterSetName = ”ID” - )] - Param ( - #Object receiving the hardening - [Parameter( - Position = 0, - ValueFromPipeline = $true, - ValueFromPipelineByPropertyName = $true, - HelpMessage = 'What object are you running this against?')] - #[string]$VMs, - [PSObject[]] - $VM, - - #Hardening Guide Profile - [Parameter(Mandatory = $false, - ValueFromPipeline = $false, - HelpMessage = "You must choose which Hardening Guide Risk Profile to use (1-3).", - ParameterSetName = 'RiskProfile')] - #[ValidateRange(1,3)] - [int]$RiskProfile, - - #Hardening Guide Guideline ID - [Parameter(Mandatory = $false, - ValueFromPipeline = $false, - ParameterSetName = 'ID')] - [string[]]$ID, - - [parameter(ParameterSetName = "ID", - ValueFromPipeline = $false)] - $AllIDs = "1" - ) - - BEGIN { - # Validate VM Parameter - # This ensures the object being passed is an object rather than a string - [int]$Err = 0 - foreach ($object in $vm) { if (($object.GetType().FullName) -ne "VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl") { $Err += 1 } } - if ($Err -gt 0) { - Write-Error "One or more objects specified in the `$VM parameter is not a VM object" - Break - } - - - # Get the Date, used for folder creation and report - $Date = (Get-Date -Format MM.dd.yy) - - # Create array of ID's - if (-not $ID) { - $IDs = $null - } else { - $IDs = $ID.Replace(' ', '').split(',') - } - - # Manage Risk Profile Parameter value - $RiskArray = @("1","2","3") - - - ### HTML CSS CODE ### - $head = @' - -'@ - ##################### - - #C heck to see if HG exists in the $Env:Temp folder, if not, place it there - if (!(Test-Path -Path "$env:Temp\HG.XLSX")) { - - # Decode Hardening Guide from script - $Content = [System.Convert]::FromBase64String($Global:Base64) - Set-Content -Path $env:temp\HG.XLSX -Value $Content -Encoding Byte - } - - #Save Imported Hardening Guide to $XLS - $Global:XLS = Import-XLS $env:temp\HG.XLSX - - } - - PROCESS { - - # If risk profile is provided - if ($RiskArray -contains $RiskProfile) { - - $Guidelines = ($XLS | ?{ $_."Guideline ID" -like "VM*" -and $_."Risk Profile" -match $RiskProfile }) - foreach ($Guideline in $Guidelines) { - - $ID = $Guideline.ID - $GuidelineID = $Guideline."Guideline ID" - $Name = ($GuidelineID.Split("."))[1] - $Assessment = $Guideline."PowerCLI Command Assessment" - $Assessment = $Assessment -replace "Get-VM", "`$VM" - $Description = $Guideline."Description" - - Write-Host "Processing: $ID - $Name" -ForegroundColor CYAN - if ($assessment -ne "") { - $run = iex ($Assessment) - if ($run -eq $null) { - $run = New-Object System.object - $run | Add-Member -type NoteProperty -name Name -value "Clear" - $run | Add-Member -type NoteProperty -name Description -value "NO SECURITY HARDENING NEEDED FOR THIS GUIDELINE" - } - $Task = $run | ConvertTo-Html -Fragment -PreContent "