Public/Update-EntityBaselineGroup.ps1

function Update-EntityBaselineGroup {
    <#
    .SYNOPSIS
        Remediates a host against a baseline group.
 
    .DESCRIPTION
        Makes a call to the VC Integrity API to remediate a host or cluster against a baseline group.
 
    .PARAMETER baselineGroupName
        Name of the baseline group to remediate against.
 
    .PARAMETER entity
        Entity object to remediate against, either a host or a cluster.
 
    .INPUTS
        PSObject An entity object for either a cluster or a host.
        Must be of type VMware.VimAutomation.ViCore.Impl.V1.Inventory.ClusterImpl or VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl
 
    .OUTPUTS
        None.
 
    .EXAMPLE
        $VMHost = Get-VMHost -name "esxi01"
        Update-EntityBaselineGroup -BaselineGroupName "Sample Baseline Group" -Entity $VMHost
 
        Remediates host esxi01 against baseline group Sample Baseline Group.
 
    .EXAMPLE
        $Cluster = Get-Cluster -name "vSAN"
        Update-EntityBaselineGroup -BaselineGroupName "Sample Baseline Group" -Entity $Cluster
 
        Remediates cluster vSAN against baseline group Sample Baseline Group.
 
    .EXAMPLE
        $vmHosts | Update-EntityBaselineGroup -baselineGroupName "Test-BaselineGroup01" -Verbose
 
        Remediates all hosts in $vmHosts, one at a time, to baseline group Test-Baselinegroup01
 
    .LINK
        https://github.com/TheDotSource/VUMXtra
 
    .NOTES
        01 13/11/18 Initial version. A McNair
        02 23/12/19 Tidied up synopsis and added verbose output. A McNair
                              Added pipeline input for entity.
    #>


    [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="High")]
    Param
    (
        [Parameter(Mandatory=$true,ValueFromPipeline=$false)]
        [String]$baselineGroupName,
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [ValidateScript({($_.GetType().toString() -eq "VMware.VimAutomation.ViCore.Impl.V1.Inventory.ClusterImpl") -or ($_.GetType().toString() -eq "VMware.VimAutomation.ViCore.Impl.V1.Inventory.VMHostImpl")})]
        [PSObject]$entity
    )

    begin {

        Write-Verbose ("[Update-EntityBaselineGroup]Function start.")

        ## Get a VUM service connection object
        try {
            $vumCon = Connect-VUM -ErrorAction stop
            Write-Verbose ("[Update-EntityBaselineGroup]Got VUM connection.")
        } # try
        catch {
            Write-Debug ("[Update-EntityBaselineGroup]Failed to connect to VUM instance.")
            throw ("Failed to connect to VUM instance. The CMDlet returned " + $_)
        } # catch


        ## Get the baseline group object
        for ($i=0; $i -le 100; $i++) {

            ## When baseline is found break out of loop to continue function
            if (($vumCon.vumWebService.GetBaselineGroupInfo($vumCon.vumServiceContent.baselineGroupManager,$i)).name -eq $baselineGroupName) {

                $BaselineGroup = $vumCon.vumWebService.GetBaselineGroupInfo($vumCon.vumServiceContent.baselineGroupManager,$i)
                Write-Verbose ("[Update-EntityBaselineGroup]Found baseline group " + $baselineGroupName)
                Break

            } # if

        } # for


        ## Check we have a baseline group to work with
        if (!$baselineGroup) {
            Write-Debug ("[Update-EntityBaselineGroup]Baseline group not found.")
            throw ("The specified baseline group was not found on this VUM instance.")
        } # if

    } # begin

    process {

        Write-Verbose ("[Update-EntityBaselineGroup]Processing entity " + $entity.name)


        ## Initiate a scan of the host
        try {
            Test-Compliance -Entity $Entity -ErrorAction Stop | Out-Null
        } # try
        catch {
            Write-Debug ("[Update-EntityBaselineGroup]Failed to scan entity.")
            throw ("Compliance scan failed on entity. " + $_)
        } # catch

        Write-Verbose ("[Update-EntityBaselineGroup]Completed compliance scan of entity.")


        ## Set parent and leaf objects
        $LeafTypeValue = $Entity.id.split("-",2)
        $LeafEntity = New-Object IntegrityApi.ManagedObjectReference
        $LeafEntity.type = $LeafTypeValue[0]
        $LeafEntity.Value = $LeafTypeValue[1]

        $ParentTypeValue = $Entity.ParentId.split("-",2)
        $ParentEntity = New-Object IntegrityApi.ManagedObjectReference
        $ParentEntity.type = $ParentTypeValue[0]
        $ParentEntity.Value = $ParentTypeValue[1]

        Write-Verbose ("[Update-EntityBaselineGroup]Entity object configured.")



        ## Query compliance status for specified baseline group
        try {
            $complianceStatus = $vumCon.vumWebService.QueryBaselineGroupComplianceStatus($vumCon.vumServiceContent.complianceStatusManager,$LeafEntity) | Where-Object {$_.key -eq $BaselineGroup.key}
            Write-Verbose ("[Update-EntityBaselineGroup]Obtained entity compliance status.")
        } # try
        catch {
            Write-Debug ("[Update-EntityBaselineGroup]Failed to get compliance status.")
            throw ("Failed to query compliance status of entity. " + $_)
        } # catch


        ## Check if this entity is compliant with baseline group or not
        if ($ComplianceStatus.status -eq "Compliant") {
            Write-Verbose ("[Update-EntityBaselineGroup]Entity is already compliant with baseline group.")
            Write-Warning ("Entity is already compliant with baseline group, no further action will be taken.")
            Break
        } # if


        ## Phase 1 remediation
        ## Initialise IntegrityApi.HostRemediationScheduleOption object and configure
        $hostScheduler = New-Object IntegrityApi.HostRemediationScheduleOption
        $hostScheduler.failureAction = "Retry" # Possible values, FailTask, Retry
        $hostScheduler.updateHostTime = Get-Date
        $hostScheduler.updateHostTimeSpecified = $false
        $hostScheduler.evacuationTimeout = 0
        $hostScheduler.evacuationTimeoutSpecified = $false
        $hostScheduler.evacuateOfflineVMs = $false
        $hostScheduler.evacuateOfflineVMsSpecified = $true
        $hostScheduler.preRemediationPowerAction = "DoNotChangeVMsPowerState" # Possible values, PowerOffVMs, SuspendVMs, DoNotChangeVMsPowerState
        $hostScheduler.retryDelay = 300
        $hostScheduler.retryDelaySpecified = $true
        $hostScheduler.numberOfRetries = 3
        $hostScheduler.numberOfRetriesSpecified = $true
        $hostScheduler.scheduledTaskName = "VUM Extra remediation."
        $hostScheduler.scheduledTaskDescription = "Test"
        $hostScheduler.disconnectRemovableDevices = $false
        $hostScheduler.disconnectRemovableDevicesSpecified = $true
        $hostScheduler.disableDpm = $true
        $hostScheduler.disableDpmSpecified = $true
        $hostScheduler.disableHac = $false
        $hostScheduler.disableHacSpecified = $true
        $hostScheduler.disableFt = $false
        $hostScheduler.disableFtSpecified = $true
        $hostScheduler.concurrentRemediationInCluster = $false
        $hostScheduler.concurrentRemediationInClusterSpecified = $true
        $hostScheduler.allowStatelessRemediation = $false
        $hostScheduler.allowStatelessRemediationSpecified = $true
        $hostScheduler.maxHostsForParallelRemediationInCluster = 1
        $hostScheduler.maxHostsForParallelRemediationInClusterSpecified = $true
        Write-Verbose ("[Update-EntityBaselineGroup]Host scheduler options set.")

        ## Initialise IntegrityApi.HostUpgradeOptionManagerOptions object and configure
        $hostUpgradeOptions = New-Object IntegrityApi.HostUpgradeOptionManagerOptions
        $hostUpgradeOptions.ignore3rdPartyModules = $false
        $hostUpgradeOptions.ignore3rdPartyModulesSpecified = $true
        Write-Verbose ("[Update-EntityBaselineGroup]Host upgrade options set.")


        ## Phase 2 remediation
        ## Initialise IntegrityApi.VcIntegrityRemediateOption object, consuming phase 1 objects as input
        ## Initialise IntegrityApi.UpdateManagerBaselineGroupUnit which specifies what baseline group and baselines we want to use
        $RemediateOption = New-Object IntegrityApi.VcIntegrityRemediateOption
        $RemediateOption.hostScheduler = $hostScheduler
        $RemediateOption.hostUpgradeOptions = $hostUpgradeOptions
        $BaselineGroupUnit = New-Object IntegrityApi.UpdateManagerBaselineGroupUnit
        $BaselineGroupUnit.baselinegroup = $BaselineGroup.Key
        Write-Verbose ("[Update-EntityBaselineGroup]Remediation option and group unit objects configured.")


        ## Phase 3 remediation
        ## Initialise IntegrityApi.UpdateManagerRemediationSpec object which consumes phase 2 objects as input
        $RemediationSpec = New-Object IntegrityApi.UpdateManagerRemediationSpec
        $RemediationSpec.baselineGroupUnit = $BaselineGroupUnit
        $RemediationSpec.option = $RemediateOption
        Write-Verbose ("[Update-EntityBaselineGroup]Remediation spec configured.")


        ## Phase 4 remediation
        ## The phase 3 object is the completed object we send to the API with Leaf and Entity objects to the UpdateManager
        ## The API will kick back a job object we can monitor for progress
        try {

            ## Apply shouldProcess
            if ($PSCmdlet.ShouldProcess($entity.name)) {

                $MofTask = $vumCon.vumWebService.Remediate_Task($vumCon.vumServiceContent.updateManager, $ParentEntity, $LeafEntity, $RemediationSpec)
            } # if

            Write-Verbose ("[Update-EntityBaselineGroup]Remediation task started.")
        } # try
        catch {
            Write-Debug ("[Update-EntityBaselineGroup]Failed to start remediation task.")
            throw ("Failed to start remediation task. " + $_)
        } # catch


        ## Wait 5 seconds to give task a chance to start
        Start-Sleep 5

        ## Wait for remedaition jopb to complete
        try {
            $jobStatus = Get-Task -id ("Task-" + $MofTask.value) -ErrorAction Stop
        } # try
        catch {
            throw ("Failed to get task object for task " + $MofTask.value)
        } # catch

        Write-Verbose ("[Update-EntityBaselineGroup]Waiting for task to complete.")

        while ($jobStatus.State -eq "Running") {

            Write-Progress -Activity ("Applying Baseline Group to host " + $ESXiHost) -Status ($JobStatus.PercentComplete.ToString() + " percent complete.") -PercentComplete $JobStatus.PercentComplete

            Write-Verbose ("[Update-EntityBaselineGroup]Current task status is " + $jobStatus.State)

            Start-Sleep 10

            try {
                $jobStatus = Get-Task -id ("Task-" + $MofTask.value) -ErrorAction Stop
            } # try
            catch {
                throw ("Failed to get task object for task " + $MofTask.value)
            } # catch

        } # while


        Write-Verbose ("[Update-EntityBaselineGroup]Task completed, verifying result.")


        ## Check the job did not fail
        if ($JobStatus.state -eq "Error") {
            Write-Debug ("[Update-EntityBaselineGroup]Remediation task failed.")
            throw ("Remediation task failed.")
        }

        Write-Verbose ("[Update-EntityBaselineGroup]Task completed successfully.")


        Write-Verbose ("[Update-EntityBaselineGroup]Completed entity " + $entity.name)

    } # process


    end {

        Write-Verbose ("[Update-EntityBaselineGroup]All entities completed.")

        ## Logoff session
        try {
            $vumCon.vumWebService.VciLogout($vumCon.vumServiceContent.sessionManager)
            Write-Verbose ("[Update-EntityBaselineGroup]Disconnected from VUM API.")
        } # try
        catch {
            Write-Warning ("[Update-EntityBaselineGroup]Failed to disconnect from VUM API.")
        } # catch


        Write-Verbose ("[Update-EntityBaselineGroup]Function completed.")

    } # end


} # function