sFunctions.psm1

using module .\sClass.psm1

Function MakeTobj
{
    <# Helper Function used by Protect-SRMVM and UnProtect-SRMVM #>

    param($tinfo , $VMname, $VMmoref )

    $lo = [pscustomobject]@{
        VM = $VMname
        VMMoRef = $VMmoref
        Status = $tinfo.State
        Error = $tinfo.Error.LocalizedMessage
        Task = $tinfo.Name
        TaskMoRef = $tinfo.TaskMoRef
    }
    $lo.PSObject.TypeNames.Insert(0,'SupSkiFun.SRM.Protect.Info')
    $lo
}

<#
.SYNOPSIS
Connects to the SRM instance of the currently connected VCenter
.DESCRIPTION
Connects to the SRM instance of the currently connected VCenter and its paired partner with the current
session username. Prompts for a SRM password. Password is applied locally and remotely.
Can be run on recovery or protected site.
.EXAMPLE
csrm
#>

Function csrm
{
    $CUser=$env:USERDOMAIN;$CUser=$CUser+"\";$CUser=$CUser+$env:USERNAME
    $CPass=Read-Host -AsSecureString -Prompt "Enter SRM password"
    Connect-SrmServer -SrmServerAddress $DefaultVIServer -User $CUser -Password $CPass -RemoteUser $CUser -RemotePassword $CPass
}

<#
.SYNOPSIS
Retrieves SRM Protection Groups
.DESCRIPTION
Retrieves SRM Protection Groups. Can be run on recovery or protected site.
.OUTPUTS
VMware.VimAutomation.Srm.Views.SrmProtectionGroup
.EXAMPLE
Place all the SRM Protection Groups into a variable:
$allPG = Get-SRMProtectionGroup
.EXAMPLE
Place SRM Protection Groups matching a criteria into a variable:
$myPG = Get-SRMProtectionGroup | Where-Object -Property Name -Match "DS1"
#>

function Get-SRMProtectionGroup
{
    Begin
    {
        $srmED =  $DefaultSrmServers.ExtensionData
        if(!$srmED)
        {
            Write-Output "Terminating. Session is not connected to a SRM server."
            break
        }
        $pgrps = $srmED.Protection.ListProtectionGroups()
    }

    Process
    {
        foreach ($pgrp in $pgrps)
        {
            $pnom = $pgrp.GetInfo().Name
            Add-Member -InputObject $pgrp -MemberType NoteProperty -Name "Name" -Value $pnom
        }
    }

    End
    {
        $pgrps
    }
}

<#
.SYNOPSIS
Retrieves SRM Recovery Plans
.DESCRIPTION
Retrieves SRM Recovery Plans. Can be run on recovery or protected site.
.OUTPUTS
VMware.VimAutomation.Srm.Views.SrmRecoveryPlan
.EXAMPLE
Place all the SRM Recovery Plans into a variable:
$allRP = Get-SRMRecoveryPlan
.EXAMPLE
Place SRM Recovery Plans matching a criteria into a variable:
$myRP = Get-SRMRecoveryPlan | Where-Object -Property Name -Match "CL07"
#>


Function Get-SRMRecoveryPlan
{
    [cmdletbinding()]
    param()

    Begin
    {
        $srmED =  $DefaultSrmServers.ExtensionData
        if(!$srmED)
        {
            Write-Output "Terminating. Session is not connected to a SRM server."
            break
        }
        $plans = $srmED.Recovery.ListPlans()
    }

    Process
    {
        foreach ($plan in $plans)
        {
            $pnom = $plan.GetInfo().Name
            Add-Member -InputObject $plan -MemberType NoteProperty -Name "Name" -Value $pnom
        }
    }

    End
    {
        $plans
    }
}

<#
.SYNOPSIS
Returns current state of SRM Test.
.DESCRIPTION
Returns current state of SRM Test. Can be run on the recovery or protected site.
.PARAMETER RecoveryPlan
SRM Recovery Plan. VMware.VimAutomation.Srm.Views.SrmRecoveryPlan
.INPUTS
VMware.VimAutomation.Srm.Views.SrmRecoveryPlan
.OUTPUTS
[pscustomobject] SupSkiFun.SRM.Test.Status
.EXAMPLE
$p = Get-SRMRecoveryPlan | Where-Object -Property Name -eq "PlanXYZ"
$p | Get-SRMTestState
#>

Function Get-SRMTestState
{
    [cmdletbinding()]
    Param
    (
        [Parameter (Mandatory = $true , ValueFromPipeline = $true )]
        [VMware.VimAutomation.Srm.Views.SrmRecoveryPlan[]] $RecoveryPlan
    )

    Process
    {
        foreach ($rp in $RecoveryPlan)
        {
            $st = $rp.GetInfo().State
            $lo = [pscustomobject]@{
                Name = $rp.Name
                State = $st
            }
            $lo.PSObject.TypeNames.Insert(0,'SupSkiFun.SRM.Test.Status')
            $lo
        }
    }
}

<#
.SYNOPSIS
Obtains SRM VM Protection Information
.DESCRIPTION
Returns an object of VM, VMMoRef, Status, DataStore, ProtectionGroup, RecoveryPlan, ProtectedVM and PeerProtectedVm.
Run on protected site to obtain full information. Can be run on recovery site, but information is limited.
.PARAMETER VM
Output from VMWare PowerCLI Get-VM. See Examples.
[VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]
.INPUTS
VMWare PowerCLI VM from Get-VM:
[VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]
.OUTPUTS
[pscustomobject] SupSkiFun.SRM.VM.Info
.EXAMPLE
Returns object for one VM to the screen:
Get-VM -Name Server01 | Get-SRMVM
.EXAMPLE
Places an object of several VMs into a variable:
$myVar = Get-VM -Name Test* | Get-SRMVM
#>

function Get-SRMVM
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true , ValueFromPipeline = $true)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]]$VM
    )

    Begin
    {
        $srmED = $DefaultSrmServers.ExtensionData
        if(!$srmED)
        {
            Write-Output "Terminating. Session is not connected to a SRM server."
            break
        }
        $pgroups = $srmED.Protection.ListProtectionGroups()
        $pghash = [sClass]::MakePgHash($pgroups)
        $nd = "No Data"
    }

    Process
    {
        foreach ($v in $vm)
        {

            $VMdsID = $v.ExtensionData.DataStore
            $VMmoref = $v.ExtensionData.Moref
            $VMname = $v.Name
            switch ($VMdsID)
            # Switch loops if more than one $VMdsID.
            {
                {$pghash.ContainsKey($($_)) -eq $false}
                {
                    $VMdsName = (Get-Datastore -Id $_).Name
                    $reason = "Protection Group not found for DataStore $VMdsName($_) ."
                    $lo = [sClass]::MakeObj( $reason , $VMname , $VMmoref , $VMdsName , $nd )
                    $lo
                }

                {$pghash.ContainsKey($($_)) -eq $true}
                {
                    $targetpg = $pghash.Item($($_))
                    $protstat = $targetpg.QueryVmProtection($VMmoref)
                    $VMdsName = (Get-Datastore -Id $_).Name
                    $lo = [sClass]::MakeObj( $protstat , $VMname , $VMmoref , $VMdsName )
                    $lo
                }
            }
            $protstat , $lo  = $null
        }
    }
}

<#
.SYNOPSIS
Protects SRM VMs
.DESCRIPTION
Protects SRM VMs. Must be run on the protected site.
Attempts only if VM Protection State is "CanBeProtected" and an affiliated Protection Group can be located.
.PARAMETER VM
Output from VMWare PowerCLI Get-VM. See Examples.
[VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]
.INPUTS
VMWare PowerCLI VM from Get-VM:
[VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]
.OUTPUTS
[pscustomobject] SupSkiFun.SRM.Protect.Info
.EXAMPLE
Protect one VM:
Get-VM -Name SYS01 | Protect-SRMVM
.EXAMPLE
Protect multiple VMS, returning the object into a variable:
$myVar = Get-VM -Name WEB* | Protect-SRMVM
#>

Function Protect-SRMVM
{
    [cmdletbinding()]
    Param
    (
        [Parameter(Mandatory = $true , ValueFromPipeline = $true)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]]$VM
    )

    Begin
    {
        $srmED = $DefaultSrmServers.ExtensionData
        if(!$srmED)
        {
            Write-Output "Terminating. Session is not connected to a SRM server."
            break
        }
        $stat = "CanBeProtected"
        $pgroups = $srmED.Protection.ListProtectionGroups()
        $pghash = [sClass]::MakePgHash($pgroups)
    }


    Process
    {
        Function ProtVM
        {
            param($targetpg,$VMmoref)

            $vspec = [VMware.VimAutomation.Srm.Views.SrmProtectionGroupVmProtectionSpec]::new()
            $vspec.Vm = $VMmoref
            $ptask = $targetpg.ProtectVms($vspec)
            while(-not $ptask.IsComplete())
            {
                Start-Sleep -Seconds 1
            }
            $pinfo = $ptask.getresult()
            $pinfo
        }

        foreach ($v in $vm)
        {
            $VMdsID = $v.ExtensionData.DataStore
            $VMmoref = $v.ExtensionData.Moref
            $VMname = $v.Name
            switch ($VMdsID)
            # Switch loops if more than one $VMdsID.
            {
                {$pghash.ContainsKey($($_)) -eq $false}
                {
                    $VMdsName = (Get-Datastore -Id $_).Name
                    $reason = "Protection Group not found for DataStore $VMdsName($_) ."
                    $lo = [sClass]::MakeObj( $reason , $VMname , $VMmoref )
                    $lo
                }

                {$pghash.ContainsKey($($_)) -eq $true}
                {
                    $targetpg = $pghash.Item($($_))
                    $protstat = $targetpg.QueryVmProtection($VMmoref)
                    if ($protstat.Status -match $stat)
                    {
                        $tinfo = ProtVM -targetpg $targetpg -VMmoref $VMmoref
                        $lo = MakeTObj -tinfo $tinfo -VMname $VMname -VMmoref $VMmoref
                        $lo
                    }
                    else
                    {
                        $reason = "State is $($protstat.Status). State should be $stat."
                        $lo = [sClass]::MakeObj( $reason , $VMname , $VMmoref )
                        $lo
                    }
                    break
                }
            }

            $tinfo , $lo  = $null
        }
    }
}

<#
.SYNOPSIS
Sends a Dismiss command to a SRM Recovery Plan
.DESCRIPTION
Sends a Dismiss command to a SRM Recovery Plan Prompt to continue plan execution.
Does not attempt if submitted plan is not in a Prompting state. Must be run on the recovery site.
.PARAMETER RecoveryPlan
SRM Recovery Plan. VMware.VimAutomation.Srm.Views.SrmRecoveryPlan
.INPUTS
VMware.VimAutomation.Srm.Views.SrmRecoveryPlan
.EXAMPLE
$p = Get-SRMRecoveryPlan | Where-Object -Property Name -eq "PlanXYZ"
$p | Send-SRMDismiss
#>

Function Send-SRMDismiss
{
    [cmdletbinding(SupportsShouldProcess = $True , ConfirmImpact = "High")]
    Param
    (
        [Parameter (Mandatory = $true , ValueFromPipeline = $true)]
        [VMware.VimAutomation.Srm.Views.SrmRecoveryPlan[]] $RecoveryPlan
    )

    Begin
    {
        $ReqState = "Prompting"
    }
    Process
    {
        foreach ($rp in $RecoveryPlan)
        {
            $rpinfo = $rp.GetInfo()

            if ($pscmdlet.ShouldProcess( $rpinfo.Name , "Dismiss Prompt" ))
            {
                if ($rpinfo.State -eq $ReqState)
                {
                    $rp.AnswerPrompt($rp.ListPrompts().key , $false, "Dismiss")
                }

                else
                {
                    $mesg = "Not Sending Dismissal for $($rpinfo.Name). State is $($rpinfo.State). State should be $ReqState."
                    Write-Output "`n`t`t$mesg`n"
                }
            }
        }
    }
}

<#
.SYNOPSIS
Returns an object of Protected VMs.
.DESCRIPTION
Returns an object of Protected VMs from submitted Protection Groups.
Returns an object of VM, VMMoRef, ProtectedVM, PeerProtectedVm, ProtectionGroup, VMState,
PeerState, NeedsConfig and Faults. Can be run on recovery or protected site.
Note: VM Name is Not Available from the Recovery Site; it is only available from the Protection Site.
.PARAMETER ProtectionGroup
[VMware.VimAutomation.Srm.Views.SrmProtectionGroup] See Examples.
.INPUTS
[VMware.VimAutomation.Srm.Views.SrmProtectionGroup]
.OUTPUTS
[pscustomobject] SupSkiFun.SRM.Protect.Info
.EXAMPLE
Return an object of VMs from specific Protection Group(s) into the myInfo variable:
$myPG = Get-SRMProtectionGroup | Where-Object -Property Name -Match "DS1"
$myInfo = $myPG | Show-SRMProtectedVM
.EXAMPLE
Return an object of VMs from all Protection Groups into the myInfo variable:
$myPG = Get-SRMProtectionGroup
$myInfo = $myPG | Show-SRMProtectedVM
#>

function Show-SRMProtectedVM
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true , ValueFromPipeline = $true)]
        [VMware.VimAutomation.Srm.Views.SrmProtectionGroup[]] $ProtectionGroup
    )

    Begin
    {
        $nota = "Not Available on Recovery Site; only available from the Protection Site"
    }

    Process
    {
        foreach ($pgrp in $ProtectionGroup)
        {
            $pvms = $pgrp.ListProtectedVms()
            $pgst = $pgrp.GetProtectionState()
            foreach ($pvm in $pvms)
            {
                switch ($pgst)
                {
                    Ready
                    {
                        $pvm.vm.UpdateViewData()
                        $vmnom = $pvm.Vm.Config.Name
                    }
                    Shadowing
                    {
                        $vmnom = $nota
                    }
                }

                $lo = [pscustomobject]@{
                    VM = $vmnom
                    VMMoRef = $pvm.Vm.Moref
                    ProtectedVM = $pvm.ProtectedVm
                    PeerProtectedVm = $pvm.PeerProtectedVm
                    ProtectionGroup = $pgrp.Name
                    VMState = $pvm.State
                    PeerState = $pvm.PeerState
                    NeedsConfig = $pvm.NeedsConfiguration
                    Faults = $pvm.Faults
                }
            $lo.PSObject.TypeNames.Insert(0,'SupSkiFun.SRM.Protect.Info')
            $lo
            }
        }
    }
}

<#
.SYNOPSIS
Shows Relationship amongst Recovery Plans, Protection Groups, and DataStores.
.DESCRIPTION
Shows Relationship amongst Recovery Plans, Protection Groups, and DataStores. Returns an object of RecoveryPlan,
ProtectionGroup, DataStore, RecoveryPlanMoRef, ProtectionGroupMoref, DataStoreMoRef, PeerMoRef and PeerState.
Can be run on recovery or protected site.
Note: DataStore Name is Not Available from the Recovery Site; it is only available from the Protection Site.
.PARAMETER RecoveryPlan
SRM Recovery Plan. VMware.VimAutomation.Srm.Views.SrmRecoveryPlan
.INPUTS
VMware.VimAutomation.Srm.Views.SrmRecoveryPlan
.OUTPUTS
PSCUSTOMOBJECT SupSkiFun.SRM.Info
.EXAMPLE
Show Relationship of all SRM Recovery Plans returning object into a variable:
$allRP = Get-SRMRecoveryPlan
$MyVar = $allRP | Show-SRMRelationship
.EXAMPLE
Show Relationship of specific SRM Recovery Plan(s) matching a criteria, returning object into a variable:
$myRP = Get-SRMRecoveryPlan | Where-Object -Property Name -Match "CL07*"
$MyVar = $myRP | Show-SRMRelationship
#>

Function Show-SRMRelationship
{
    [cmdletbinding()]
    Param
    (
        [Parameter (Mandatory = $true, ValueFromPipeline = $true)]
        [VMware.VimAutomation.Srm.Views.SrmRecoveryPlan[]] $RecoveryPlan
    )

    Begin
    {
        $nota = "Not Available on Recovery Site; only available from the Protection Site"
    }

    Process
    {
        foreach ($plan in $RecoveryPlan)
        {
            $ap = $plan.GetInfo()
            $pg = $ap.ProtectionGroups.GetInfo().Name
            $ar = $ap.ProtectionGroups.ListProtectedDatastores().Moref
            $pi = $plan.GetPeer()

            if ($ap.State -match "Protecting")
            {
                $ds = (get-datastore -id $ar).Name
            }
            else
            {
                $ds = $nota
            }

            $lo = [pscustomobject]@{
                RecoveryPlan = $ap.Name
                RecoveryPlanState = $ap.State
                ProtectionGroup = $pg
                Datastore = $ds
                RecoveryPlanMoRef = $plan.MoRef
                ProtectionGroupMoRef = $ap.ProtectionGroups.Moref
                DataStoreMoRef = $ar
                PeerMoRef = $pi.PlanMoRef
                PeerState = $pi.State
            }
            $lo.PSObject.TypeNames.Insert(0,'SupSkiFun.SRM.Info')
            $lo
        }
    }
}

<#
.SYNOPSIS
Starts the SRM Cleanup Process.
.DESCRIPTION
Starts the SRM Cleanup Process for specified SRM Recovery Plans.
Does not attempt if submitted plan is not in a NeedsCleanup state. Must be run on the recovery site.
.PARAMETER RecoveryPlan
SRM Recovery Plan. VMware.VimAutomation.Srm.Views.SrmRecoveryPlan
.INPUTS
VMware.VimAutomation.Srm.Views.SrmRecoveryPlan
.EXAMPLE
$p = Get-SRMRecoveryPlan | Where-Object -Property Name -eq "PlanXYZ"
$p | Start-SRMCleanUp
#>

Function Start-SRMCleanUp
{
    [cmdletbinding(SupportsShouldProcess = $True , ConfirmImpact = "High")]
    Param
    (
        [Parameter (Mandatory = $true , ValueFromPipeline = $true )]
        [VMware.VimAutomation.Srm.Views.SrmRecoveryPlan[]] $RecoveryPlan
    )

    Begin
    {
        [VMware.VimAutomation.Srm.Views.SrmRecoveryPlanRecoveryMode] $RecoveryMode = [VMware.VimAutomation.Srm.Views.SrmRecoveryPlanRecoveryMode]::CleanUpTest
        $ReqState = "NeedsCleanup"
    }

    Process
    {
        foreach ($rp in $RecoveryPlan)
        {
            $rpinfo = $rp.GetInfo()

            if ($pscmdlet.ShouldProcess($rpinfo.Name, $RecoveryMode))
            {
                if ($rpinfo.State -eq $ReqState)
                {
                    $rp.Start($RecoveryMode)
                }

                else
                {
                    $mesg = "Not Starting Cleanup for $($rpinfo.Name). State is $($rpinfo.State). State should be $ReqState."
                    Write-Output "`n`t`t$mesg`n"
                }
            }
        }
    }
}

<#
.SYNOPSIS
Starts a Test SRM Recovery Plan.
.DESCRIPTION
Starts a Test SRM Recovery Plan, optionally synching data.
Does not attempt if submitted plan is not in a Ready state. Must be run on the recovery site.
.PARAMETER RecoveryPlan
SRM Recovery Plan. VMware.VimAutomation.Srm.Views.SrmRecoveryPlan
.PARAMETER SyncData
Future: Defaults to False. Can be set True to Sync Data. Believe exposed in SRM 6.5 API
.INPUTS
VMware.VimAutomation.Srm.Views.SrmRecoveryPlan
.EXAMPLE
$p = Get-SRMRecoveryPlan | Where-Object -Property Name -eq "PlanXYZ"
$p | Start-SRMTest
.EXAMPLE
Future Functionality Below:
$p = Get-SRMRecoveryPlan | Where-Object -Property Name -eq "PlanXYZ"
$p | Start-SRMTest -SyncData=$False
#>


Function Start-SRMTest
{
    [cmdletbinding(SupportsShouldProcess = $True , ConfirmImpact = "High")]
    Param
    (
        [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)]
        [VMware.VimAutomation.Srm.Views.SrmRecoveryPlan[]] $RecoveryPlan,

        [bool] $SyncData = $False
    )

    Begin
    {
        [VMware.VimAutomation.Srm.Views.SrmRecoveryPlanRecoveryMode] $RecoveryMode = [VMware.VimAutomation.Srm.Views.SrmRecoveryPlanRecoveryMode]::Test
        $ReqState = "Ready"

        <#
        Below two lines for creating the option to synch or not. Believe exposed in SRM 6.5 API
        Also modify the entry in the process block.
        $rpOpt = [VMware.VimAutomation.Srm.Views.SrmRecoveryOptions]::new()
        $rpOpt.SyncData = [bool] $SyncData
        #>


    }

    Process
    {
        foreach ($rp in $RecoveryPlan)
        {
            $rpinfo = $rp.GetInfo()

            if ($pscmdlet.ShouldProcess($rpinfo.Name, $RecoveryMode))
            {
                if ($rpinfo.State -eq $ReqState)
                {
                    <#
                    With Synch false purposely set, API 6.5 or Higher hopefully.
                    $rp.Start($RecoveryMode,$rpOpt)
                    #>


                    $rp.Start($RecoveryMode)
                }

                else
                {
                    $mesg = "Not Starting Test of $($rpinfo.Name). State is $($rpinfo.State). State should be $ReqState."
                    Write-Output "`n`t`t$mesg`n"
                }
            }
        }
    }
}

<#
.SYNOPSIS
Stops / cancels an SRM Test.
.DESCRIPTION
Stops / cancels an SRM Test for specified SRM Recovery Plans.
Does not attempt if submitted plan is not in a Running or Prompting state. Must be run on the recovery site.
.PARAMETER RecoveryPlan
SRM Recovery Plan. VMware.VimAutomation.Srm.Views.SrmRecoveryPlan
.INPUTS
VMware.VimAutomation.Srm.Views.SrmRecoveryPlan
.EXAMPLE
$p = Get-SRMRecoveryPlan | Where-Object -Property Name -eq "PlanXYZ"
$p | Stop-SRMTest
#>

Function Stop-SRMTest
{
    [cmdletbinding(SupportsShouldProcess = $True , ConfirmImpact = "High")]
    Param
    (
        [Parameter (Mandatory = $true , ValueFromPipeline = $true )]
        [VMware.VimAutomation.Srm.Views.SrmRecoveryPlan[]] $RecoveryPlan
    )

    Begin
    {
        $ReqState = "Running" , "Prompting"
    }

    Process
    {
        foreach ($rp in $RecoveryPlan)
        {
            $rpinfo = $rp.GetInfo()

            if ($pscmdlet.ShouldProcess($rpinfo.Name, 'Cancel'))
            {
                if ($rpinfo.State -in $ReqState)
                {
                    $rp.Cancel()
                }

                else
                {
                    $mesg = "Not Stopping $($rpinfo.Name). State is $($rpinfo.State). State should be $($ReqState -join " or ")."
                    Write-Output "`n`t`t$mesg`n"
                }
            }
        }
    }
}

<#
.SYNOPSIS
UnProtects SRM VMs
.DESCRIPTION
UnProtects SRM VMs. Must be run on the protected site.
Attempts only if VM Protection State is "IsProtected" and an affiliated Protection Group can be located.
.PARAMETER VM
Output from VMWare PowerCLI Get-VM. See Examples.
[VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]
.INPUTS
VMWare PowerCLI VM from Get-VM:
[VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]
.OUTPUTS
[pscustomobject] SupSkiFun.SRM.Protect.Info
.EXAMPLE
UnProtect one VM:
Get-VM -Name SYS01 | UnProtect-SRMVM
.EXAMPLE
UnProtect multiple VMS, returning the object into a variable:
$myVar = Get-VM -Name WEB* | UnProtect-SRMVM
#>

Function UnProtect-SRMVM
{
    [cmdletbinding()]
    Param
    (
        [Parameter(Mandatory = $true , ValueFromPipeline = $true)]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine[]]$VM
    )

    Begin
    {
        $srmED = $DefaultSrmServers.ExtensionData
        if(!$srmED)
        {
            Write-Output "Terminating. Session is not connected to a SRM server."
            break
        }
        $stat = "IsProtected"
        $pgroups = $srmED.Protection.ListProtectionGroups()
        $pghash = [sClass]::MakePgHash($pgroups)
    }

    Process
    {
            Function UnProtVM
            {
                param($targetpg,$VMmoref)

                $ptask = $targetpg.UnProtectVms($VMmoref)
                while(-not $ptask.IsComplete())
                {
                    Start-Sleep -Seconds 1
                }
                $pinfo = $ptask.getresult()
                $pinfo
            }

            foreach ($v in $vm)
            {
                $VMdsID = $v.ExtensionData.DataStore
                $VMmoref = $v.ExtensionData.Moref
                $VMname = $v.Name
                switch ($VMdsID)
                # Switch loops if more than one $VMdsID.
                {
                    {$pghash.ContainsKey($($_)) -eq $false}
                    {
                        $VMdsName = (Get-Datastore -Id $_).Name
                        $reason = "Protection Group not found for DataStore $VMdsName($_) ."
                        $lo = [sClass]::MakeObj( $reason , $VMname , $VMmoref )
                        $lo
                    }

                    {$pghash.ContainsKey($($_)) -eq $true}
                    {
                        $targetpg = $pghash.Item($($_))
                        $protstat = $targetpg.QueryVmProtection($VMmoref)
                        if ($protstat.Status -match $stat)
                        {
                            $tinfo = UnProtVM -targetpg $targetpg -VMmoref $VMmoref
                            $lo = MakeTObj -tinfo $tinfo -VMname $VMname -VMmoref $VMmoref
                            $lo
                        }

                        else
                        {
                            $reason = "State is $($protstat.Status). State should be $stat."
                            $lo = [sClass]::MakeObj( $reason , $VMname , $VMmoref )
                            $lo
                        }
                        break
                    }
                }

            $tinfo , $lo  = $null
        }
    }
}

Export-ModuleMember -Function * -Alias * -Cmdlet * -Variable *