Import-LocalizedData -BindingVariable stringTable


#########################################################################################
# Copyright (c) Microsoft Corporation
#
# BitLocker PowerShell Module
#
#
#########################################################################################

$E_NOTFOUND = 2147943568
$E_KEYREQUIRED = 2150694941
$E_AUTOUNLOCK_ENABLED = 2150694953
$E_VOLUMEBOUND = 2150694943
$E_FAIL = 2147500037
$TPM_E_DEACTIVATED = 2150105094
$FVE_E_NOT_DECRYPTED = 2150694969
$S_FALSE = 1

$FVE_HARDWARE_TEST_NOT_FAILED_OR_PENDING = 0
$FVE_HARDWARE_TEST_FAILED = 1
$FVE_HARDWARE_TEST_PENDING = 2

$DEFAULT_DISCOVERY_VOLUME_TYPE = "<default>"

$MINIMUM_REQUIRED_RECOVERY_PROTECTORS_WITH_TPM = 2
$MINIMUM_REQUIRED_RECOVERY_PROTECTORS_WITHOUT_TPM = 3

$FVE_CONV_FLAG_DATAONLY = 1

$FVE_FORCE_ENCRYPTION_TYPE_UNSPECIFIED = 0
$FVE_FORCE_ENCRYPTION_TYPE_SOFTWARE = 1
$FVE_FORCE_ENCRYPTION_TYPE_HARDWARE = 2

$FVE_PROVISIONING_MODIFIER_USED_SPACE = 256

#########################################################################################
# Internal Function: Get-ExceptionForHrInternal
#
# Returns the COMException for a given HRESULT
#
# Ex: Get-ExceptionForHrInternal 2147942402
#
# Input: Unsigned integer - Must be a valid HRESULT
#
# Return: COMException class corresponding to the HRESULT.
#########################################################################################
function Get-ExceptionForHrInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [System.UInt32]
          $HrUInt32)
    process
    {
            $HrHexString = [string]::Format("0x{0:X}", $HrUInt32)
            $ExceptionForHr = [System.Runtime.InteropServices.Marshal]::GetExceptionForHR($HrHexString)

            $ExceptionForHr
    }
}

#########################################################################################
# Internal Function: IsNanoPowerShell
#
# Determines if this module is running on Nano PowerShell instead of full PowerShell.
#
# Return: Boolean describing the PowerShell environment.
#
#########################################################################################

function IsNanoPowerShell
{
    if ($PSEdition -eq "Core") 
    { 
        return $true
    }
    else
    {
        return $false
    }
}

#########################################################################################
# Internal Function: Decrypt-SecureStringInternal
#
# Returns a clear text string that had been protected by a SecureString.
#
# Input: SecureString - a password
#
# Return: String contained within the SecureString
#########################################################################################
function Decrypt-SecureStringInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [System.Security.SecureString]
          $SecurePassword
    )
    process
    {
        if (IsNanoPowerShell)
        {
            $intPtr = [System.Security.SecureStringMarshal]::SecureStringToCoTaskMemUnicode($SecurePassword)
            $ClearTextPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($intPtr)
            [System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($intPtr)
        }
        else
        {
            $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword)
            $ClearTextPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
            [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
        }
        return $ClearTextPassword
    }
}

#########################################################################################
# Internal Function: Get-Win32EncryptableVolumeInternal
#
# Returns Win32_EncryptableVolume WMI objects that describes volume [or volumes]
#
# Ex: Get-Win32EncryptableVolumeInternal c: - returns volume information for drive c:
#     Get-Win32EncryptableVolumeInternal    - returns all volumes with volume information. Only for encryptable volumes
#
# Input: String - volume name. Could be: drive letter or volume id or a directory that corresponds to a mounted volume.
#                 This is an optional parameter.
#
# Return: WMI object [Microsoft.Management.Infrastructure.CimInstance] that is a Win32_EncryptableVolume
#########################################################################################
function Get-Win32EncryptableVolumeInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $false)]
          [string]
          $MountPoint)

    process
    {
        Write-Debug "Begin Get-Win32EncryptableVolumeInternal($MountPoint)"

        $Win32EncryptableVolumes = `
            Get-CimInstance `
                -Namespace "root\cimv2\Security\MicrosoftVolumeEncryption" `
                -ClassName Win32_EncryptableVolume

        if (!$Win32EncryptableVolumes)
        {
            Write-Debug "No Win32_EncryptableVolume objects have been returned by Get-CimInstance"
        }

        if (!$MountPoint)
        {
            #
            # MountPoint is not set so all Win32_EncryptableVolumes are returned
            #

            $Win32EncryptableVolume = $null

            Write-Debug "No Filtering of Win32_EncryptableVolumes"
        }
        elseif ($MountPoint -match "^[a-zA-Z]$" -or $MountPoint -match "^[a-zA-Z]:$" -or $MountPoint -match "^[a-zA-Z]:\$")
        {
            #
            # MountPoint is a drive letter followed by an optional colon with an optional slash. ex: "c" or "c:" or "c:\"
            #
            $DriveLetter = $MountPoint.TrimEnd("\")
            if (!$DriveLetter.EndsWith(":"))
            {
                $DriveLetter = $DriveLetter + ":"  # WMI needs to have the colon at the end
            }

            Write-Debug "Filtering Win32_EncryptableVolumes by $DriveLetter"

            $Win32EncryptableVolume = $Win32EncryptableVolumes | where {$_.DriveLetter -eq $DriveLetter}

            #If there is no encryptable volume then fall through and report the error
        }
        elseif ($MountPoint -match "^\\\\\?\\Volume\{[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}\}\\$")
        {
            # 
            # MountPoint is a device id. An example of a valid device id is: \\?\Volume{cb96dd9a-4f54-11e0-8d6c-806e6f6e6963}\
            #
            $DeviceId = $MountPoint

            Write-Debug "Filtering Win32_EncryptableVolumes by $DeviceId"

            $Win32EncryptableVolume = $Win32EncryptableVolumes | where {$_.DeviceId -eq $DeviceId}

            #If there is no encryptable volume then fall through and report the error
        }
        else
        {
            #
            # MountPoint is a directory that is mounted to a volume. Get the volume that it belongs to.
            #

            $MsftVolume = Get-Volume -FilePath $MountPoint

            if (!$MsftVolume)
            {
                Write-Debug "No volume can be found mounted at $MountPoint"
                # Fall through and report error
            }
            else
            {
                $DeviceId = $MsftVolume.UniqueId
 
                Write-Debug "Volume at $MountPoint has Device Id $DeviceId. Filtering Win32_EncryptableVolumes by this Device Id."
            
                $Win32EncryptableVolume = $Win32EncryptableVolumes | where {$_.DeviceID -eq $DeviceId}
            }
        }


        if ($Win32EncryptableVolume)
        {
            $Win32EncryptableVolume
            Write-Debug "End Get-Win32EncryptableVolumeInternal. Return $Win32EncryptableVolume"
        }
        elseif (!$MountPoint -and $Win32EncryptableVolumes)
        {
            # $null for $MountPoint means return all encryptable volumes
            $Win32EncryptableVolumes
            Write-Debug "End Get-Win32EncryptableVolumeInternal. Return $Win32EncryptableVolumes"
        }
        else
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $E_NOTFOUND
            $ErrorMessage = [string]::Format($stringTable.ErrorMountPointNotFound, $MountPoint)
            Write-Error -Exception $ExceptionForHr -Message $ErrorMessage
        }
    }
}

#########################################################################################
# Internal Function: Read-UserSecretInternal
#
# Returns a single SecureString that is a secret [password or pin] entered
# by the user. This secret is confirmed by requiring the user to enter it
# twice and verifying that they are the same.
#
# If the two secrets are not the same then we re-prompt the user for both secrets.
# 
# Ex: Read-UserSecretInternal -Message "Enter Password:" -ConfirmMessage "Confirm Password:" -NotMatchMessage "Passwords do not match. Re-enter"
#
# Input: Message String - Output to user to enter the secret
#        Confirm Message String - Output to user to confirm secret
#        NotMessage Message String - Output to the user if the two secrets 
#                                    do not match
#
# Return: One of the SecureStrings that the user entered.
#########################################################################################
function Read-UserSecretInternal
{
    
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $Message,

          [Parameter(Position = 1, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $ConfirmMessage,

          [Parameter(Position = 2, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $NotMatchMessage)

    process
    {

        #
        # Break out of this loop when the two secrets entered by the user
        # match
        #
        while ($true) 
        {
            Write-Host $Message -NoNewLine
            $Secret1 = Read-Host -AsSecureString
            Write-Host $ConfirmMessage -NoNewLine
            $Secret2 = Read-Host -AsSecureString


            $ClearTextPassword1 = Decrypt-SecureStringInternal $Secret1
            $ClearTextPassword2 = Decrypt-SecureStringInternal $Secret2

            if ($ClearTextPassword1 -eq $ClearTextPassword2)
            {
                break
            }

            #
            # The two secrets don't match so tell the user and ask user again
            #

            Write-Host $NotMatchMessage
        }

        # Clear the clear text password strings.
        $ClearTextPassword1 = ""
        $ClearTextPassword1 = ""

        return $Secret1
    }

}

#########################################################################################
# Internal Function: Get-BitLockerVolumeInternal
#
# Returns a single BitLockerVolume structure that describes a volume
#
# Ex: Get-BitLockerVolumeInternal c: - returns volume information for drive c:
#
# Input: String - volume name. Could be: drive letter or volume id
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume
#########################################################################################
function Get-BitLockerVolumeInternal
{
    
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint)
    process
    {
        Write-Debug "Begin Get-BitLockerVolumeInternal($MountPoint)"

        $FREE_SPACE_WIPE_IN_PROGRESS     = 2
        $FREE_SPACE_WIPE_SUSPENDED       = 3
        $BYTES_IN_GIGABYTE               = 1024*1024*1024

        #######
        # Get Win32_EncryptableVolume
        #######

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint
        if (!$Win32EncryptableVolume)
        {
            Write-Debug "The following operation failed: Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint"
            return
        }

        #######
        # Get MSFT_Volume associated with Win32_EncryptableVolume.
        # Use Win32_EncryptableVolume.DeviceID
        #######

        $WmiVolumeFilter = "UniqueID = '$($Win32EncryptableVolume.DeviceID.Replace("\", "\\"))'"
        $MsftVolume =  Get-CimInstance MSFT_Volume -NameSpace 'Root\Microsoft\Windows\Storage' -Filter $WmiVolumeFilter
        if (!$MsftVolume)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $E_NOTFOUND
            $ErrorMessage = [string]::Format($stringTable.ErrorVolumeNotFound, $Win32EncryptableVolume.DeviceId)
            Write-Error -Exception $ExceptionForHr -Message $ErrorMessage

            Write-Debug "Filter: $WmiVolumeFilter"
            return
        }

        #
        # This matches WMI .Net's $Win32EncryptableVolume.__SERVER. 
        # MI .Net exposes $Win32EncryptableVolume.CimSystemProperties.ServerName as "localhost"
        #
        $ComputerName = $env:computername 

        #
        # Overwrite the passed in $MountPoint parameter with the the info from Win32_EncryptableVolume
        #
        if ($Win32EncryptableVolume.DriveLetter)
        {
            $MountPoint = $Win32EncryptableVolume.DriveLetter
        }
        else
        {
            $MountPoint = $Win32EncryptableVolume.DeviceID
        }

        #######
        # Get LockStatus. Win32_EncryptableVolume::GetLockStatus
        #######

        $LockStatusResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetLockStatus
        if ($LockStatusResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $LockStatusResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return
        }
        $LockStatus = $LockStatusResult.LockStatus

        Write-Debug "ComputerName: $ComputerName. MountPoint: $MountPoint. LockStatus: $LockStatus"
        
        #######
        # Get EncryptionMethod. Win32_EncryptableVolume::GetEncryptionMethod
        #######

        $EncryptionMethodResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetEncryptionMethod
        if ($EncryptionMethodResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $EncryptionMethodResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return
        }
        $EncryptionMethod = $EncryptionMethodResult.EncryptionMethod

        Write-Debug "EncryptionMethod: $EncryptionMethod"

        #######
        # AutoUnlock. Win32_EncryptableVolume::IsAutoUnlockEnabled
        # This will determine if autounlock is enabled for the volume and what the
        # protector id is. This is relevant only for data volumes.
        # Failure is ok. It means the setting does not apply to this volume.
        #######

        $AutoUnlockEnabledResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName IsAutoUnlockEnabled
        $AutoUnlockKeyProtectorId = $null

        if ($AutoUnlockEnabledResult.ReturnValue -ne 0)
        {
            Write-Debug "Win32EncryptableVolume.IsAutoUnlockEnabled() returned error $AutoUnlockEnabledResult.ReturnValue"
            $AutoUnlockEnabled = $null
        }
        else
        {
            $AutoUnlockEnabled = $AutoUnlockEnabledResult.IsAutoUnlockEnabled
            if ($AutoUnlockEnabled -eq $true)
            {
                $AutoUnlockKeyProtectorId = $AutoUnlockEnabledResult.VolumeKeyProtectorID
            }
        }

        Write-Debug "AutoUnlockEnabled: $AutoUnlockEnabled. AutoUnlockKeyProtectorId: $AutoUnlockKeyProtectorId"

        #######
        # AutoUnlockKeyStored. Win32_EncryptableVolume::IsAutoUnlockKeyStored
        # This will determine if autounlock keys are stored in the volume.
        # This is only applicable to OS volumes.
        # Failure is ok. It means the setting does not apply to this volume.
        #######

        $AutoUnlockKeyStoredResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName IsAutoUnlockKeyStored
        if ($AutoUnlockKeyStoredResult.ReturnValue -ne 0)
        {
            Write-Debug "Win32EncryptableVolume.IsAutoUnlockKeyStored() returned error $AutoUnlockKeyStoredResult.ReturnValue"
            $AutoUnlockKeyStored = $null
        }
        else
        {
            $AutoUnlockKeyStored = $AutoUnlockKeyStoredResult.IsAutoUnlockKeyStored
        }

        Write-Debug "AutoUnlockKeyStored: $AutoUnlockKeyStored"

        #######
        # Get MetaDataVersion. Win32_EncryptableVolume::GetVersion()
        #######

        $MetaDataVersionResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetVersion
        if ($MetaDataVersionResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $MetaDataVersionResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return
        }
        $MetaDataVersion = $MetaDataVersionResult.Version
        Write-Debug "MetaDataVersion: $MetaDataVersion"


        #######
        # Get ConversionStatus, WipingStatus, EncryptionPercentage, WipePercentage.
        # Win32_EncryptableVolume::GetConversionStatus()
        # We make these calls only on unlocked volumes. Otherwise, the values are initialized to $null
        #######

        if ($LockStatus -eq [uint32][Microsoft.BitLocker.Structures.BitLockerVolumeLockStatus]::Unlocked)
        {
            $ConversionStatusResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetConversionStatus
            if ($ConversionStatusResult.ReturnValue -ne 0)
            {
                $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $ConversionStatusResult.ReturnValue
                Write-Error -Exception $ExceptionForHr
                return
            }
            $ConversionStatus      = $ConversionStatusResult.ConversionStatus
            $WipingStatus          = $ConversionStatusResult.WipingStatus
            $EncryptionPercentage  = $ConversionStatusResult.EncryptionPercentage
            $WipePercentage        = $ConversionStatusResult.WipingPercentage
            Write-Debug "ConversionStatus: $ConversionStatus. WipingStatus: $WipingStatus. EncryptionPercentage: $EncryptionPercentage. WipePercentage: $WipePercentage"

        
            if ($ConversionStatus -eq [uint32][Microsoft.BitLocker.Structures.BitLockerVolumeStatus]::FullyEncrypted -and $WipingStatus -eq $FREE_SPACE_WIPE_IN_PROGRESS)
            {
                $VolumeStatus = [Microsoft.BitLocker.Structures.BitLockerVolumeStatus]::FullyEncryptedWipeInProgress
            }
            elseif ($ConversionStatus -eq [uint32][Microsoft.BitLocker.Structures.BitLockerVolumeStatus]::FullyEncryptedWipeInProgress -and $WipingStatus -eq $FREE_SPACE_WIPE_SUSPENDED)  
            {
                $VolumeStatus = [Microsoft.BitLocker.Structures.BitLockerVolumeStatus]::FullyEncryptedWipeSuspended
            }
            else
            {
                $VolumeStatus = $ConversionStatus
            }
        }
        else
        {
            $ConversionStatus      = $null
            $WipingStatus          = $null
            $VolumeStatus          = $null
            $WipePercentage        = $null
            $EncryptionPercentage  = $null
        }

        #######
        # Get ProtectionStatus
        # Win32_EncryptableVolume::GetProtectionStatus()
        #######

        $ProtectionStatusResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetProtectionStatus
        if ($ProtectionStatusResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $ProtectionStatusResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return
        }
        $ProtectionStatus = $ProtectionStatusResult.ProtectionStatus
        Write-Debug "ProtectionStatus: $ProtectionStatus."
        
        #######
        # Get VolumeType [OS, Data] and capacity from MSFT_Volume
        #######

        if ($MsftVolume.DriveLetter -eq $env:SystemDrive[0])
        {
            $VolumeType = [Microsoft.BitLocker.Structures.BitLockerVolumeType]::OperatingSystem
        }
        else
        {
            $VolumeType = [Microsoft.BitLocker.Structures.BitLockerVolumeType]::Data
        }

        $CapacityGB = $MsftVolume.Size / $BYTES_IN_GIGABYTE

        Write-Debug "VolumeType: $VolumeType. CapacityGB: $CapacityGB"

        #######
        # Get list of key protector ids Win32_EncryptableVolume::GetKeyProtectors
        # For each key protector id we do:
        #   - Get key protector type Win32_EncryptableVolume::GetKeyProtectorType
        #   - For Unlocked volumes we also get extra data for external key, numerical password
        #     and public key, and tpm network key.
        #######


        $KeyProtectorIdsResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetKeyProtectors
        if ($KeyProtectorIdsResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $KeyProtectorIdsResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return
        }
        $KeyProtectorIds = $KeyProtectorIdsResult.VolumeKeyProtectorID
        Write-Debug "KeyProtectorIds: $KeyProtectorIds"
        
        [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtector[]]$KeyProtectors = $null
        for ($i=0; $i -lt $KeyProtectorIds.Length; $i++)
        {
            $KeyProtectorId               = $KeyProtectorIds[$i]
            $KeyProtectorTypeResult       = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetKeyProtectorType -Arguments @{VolumeKeyProtectorID = $KeyProtectorId}
            if ($KeyProtectorTypeResult.ReturnValue -ne 0)
            {
                $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $KeyProtectorTypeResult.ReturnValue
                Write-Error -Exception $ExceptionForHr
                return
            }
            $KeyProtectorType             = $KeyProtectorTypeResult.KeyProtectorType

            Write-Debug "KeyProtector[$i] = KeyProtectorId: $KeyProtectorId. KeyProtectorType: $KeyProtectorType"

            $KeyProtectorFileName         = $null
            $KeyProtectorRecoveryPassword = $null
            $KeyProtectorThumbprint       = $null
            $KeyProtectorCertificateType  = $null
            $AutoUnlockProtector          = $null   # true or false only for external key protector type
        

            if ($LockStatus -eq [uint32][Microsoft.BitLocker.Structures.BitLockerVolumeLockStatus]::Unlocked)
            {

                if ($KeyProtectorType -eq [uint32][Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::ExternalKey)
                {
                    $KeyProtectorFileNameResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetExternalKeyFileName -Arguments @{VolumeKeyProtectorID = $keyProtectorId}
                    if ($KeyProtectorFileNameResult.ReturnValue -ne 0)
                    {
                        $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $KeyProtectorFileNameResult.ReturnValue
                        Write-Error -Exception $ExceptionForHr
                        return
                    }
                    $KeyProtectorFileName = $KeyProtectorFileNameResult.FileName

                    if ($AutoUnlockKeyProtectorId -ne $null)
                    {
                        if ($AutoUnlockKeyProtectorId -eq $KeyProtectorId)
                        {
                            $AutoUnlockProtector = $true
                        }
                        else
                        {
                            $AutoUnlockProtector = $false
                        }
                    }  
                    Write-Debug "KeyProtectorFileName: $KeyProtectorFileName. AutoUnlockProtector: $AutoUnlockProtector"

                }
                elseif ($KeyProtectorType -eq [uint32][Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::RecoveryPassword)
                {
                    $KeyProtectorRecoveryPasswordResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetKeyProtectorNumericalPassword -Arguments @{VolumeKeyProtectorID = $keyProtectorId}
                    if ($KeyProtectorRecoveryPasswordResult.ReturnValue -ne 0)
                    {
                        $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $KeyProtectorRecoveryPasswordResult.ReturnValue
                        Write-Error -Exception $ExceptionForHr
                        return
                    }
                    $KeyProtectorRecoveryPassword = $KeyProtectorRecoveryPasswordResult.NumericalPassword
                    Write-Debug "KeyProtectorRecoveryPassword found"
                }
                elseif ($KeyProtectorType -eq [uint32][Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::PublicKey -or $KeyProtectorType -eq [uint32][Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::TpmNetworkKey)
                {
                    $KeyProtectorCertificateResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetKeyProtectorCertificate -Arguments @{VolumeKeyProtectorID = $KeyProtectorId}
                    if ($KeyProtectorCertificateResult.ReturnValue -ne 0)
                    {
                        $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $KeyProtectorCertificateResult.ReturnValue
                        Write-Error -Exception $ExceptionForHr
                        return
                    }
                    $KeyProtectorThumbprint = $KeyProtectorCertificateResult.CertThumbprint
                    $KeyProtectorCertificateType = $KeyProtectorCertificateResult.CertType
                    Write-Debug "KeyProtectorThumbprint: $KeyProtectorThumbprint. KeyProtectorCertificateType: $KeyProtectorCertificateType"
                }

            }

            $KeyProtector = new-object Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtector -argumentlist $KeyProtectorId,$AutoUnlockProtector,$KeyProtectorType,$KeyProtectorFileName,$KeyProtectorRecoveryPassword,$KeyProtectorCertificateType,$KeyProtectorThumbprint
            $KeyProtectors = $KeyProtectors + $KeyProtector
        }
        

        $BitLockerVolume = new-object Microsoft.BitLocker.Structures.BitLockerVolume -argumentlist $ComputerName, $MountPoint, $EncryptionMethod, $AutoUnlockEnabled, $AutoUnlockKeyStored, $MetaDataVersion, $VolumeStatus, $ProtectionStatus, $LockStatus, $EncryptionPercentage, $WipePercentage, $VolumeType, $CapacityGB, $KeyProtectors

        $BitLockerVolume
        Write-Debug "End Get-BitLockerVolumeInternal. Return $BitLockerVolume"
    }
}

#########################################################################################
# Get-BitLockerVolume
#
# Returns BitLockerVolume structures that describes a volume [or volumes]
#
# Ex: Get-BitLockerVolume c: - returns volume information for drive c:
#     Get-BitLockerVolume    - returns volume information for all volumes that are encryptable
#
# Input: String[] - array of volume names. Could be: drive letter or volume id or mounted directory
#                   This is an optional input parameter.
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume[]
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function Get-BitLockerVolume
{

    [CmdletBinding()]
    Param(
          [Parameter(Position = 0, Mandatory = $false, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [string[]]
          $MountPoint)

    process
    {
        Write-Debug "Begin Get-BitLockerVolume($MountPoint)"

        if ($MountPoint)
        {
            $MountPoint | ForEach-Object -process {Get-BitLockerVolumeInternal -MountPoint $_}
        }
        else
        {
            $AllWin32EncryptableVolume = Get-Win32EncryptableVolumeInternal
            $AllWin32EncryptableVolume | ForEach-Object -process { if ($_.DriveLetter) {Get-BitLockerVolumeInternal -MountPoint $_.DriveLetter} else {Get-BitLockerVolumeInternal -MountPoint $_.DeviceId} }
        }
        
        Write-Debug "End Get-BitLockerVolume"
    }

}

#########################################################################################
# Suspend-BitLocker
#
# Returns BitLockerVolume structures that describes the volumes which have been suspended.
# Suspended means that the key protectors have been disabled. The drive contents are still
# encrypted and if encryption or decryption is in progress then this cmdlet will not change
# that. To stop the encryption or decryption process you need to call the WMI method
# PauseConversion.
#
# Input: String[]    - array of volume names. Could be: drive letter or volume id or mounted directory
#        RebootCount - Number of reboots until the volume has its key protectors re-enabled. 0 means 
#                      never enable key protectors automatically after a reboot. You need to Resume-BitLocker.
#                      For data volumes, it doesn't make sense to specify a value; if you do the WMI layer
#                      returns an error. For OS volumes, the default
#                      is determined by the WMI layer which is 1.
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume[]
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function Suspend-BitLocker
{
    [CmdletBinding(SupportsShouldProcess=$true)]

    Param(
          [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string[]]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $false)]
          [ValidateRange(0,15)]
          [int]
          $RebootCount = -1)

    process
    {
       Write-Debug "Begin Suspend-BitLocker($MountPoint, $RebootCount)"

       #########
       # ValidMountPoint is a subset of the elements of MountPoint array.
       # If MountPoint array contains an element that is not a valid mount point whose protectors
       # can be disabled then the mount point is not part of ValidMountPoint
       # Only those BitLockerVolume structures are returned that are part of ValidMountPoint
       #
       # If "-whatif" is used then ValidMountPoint is always $null
       #########
       [string[]]$ValidMountPoint = $null

       for($i=0; $i -lt $MountPoint.Count; $i++)
       {
            $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint[$i]
            if (!$BitLockerVolumeInternal)
            {
                $m = $MountPoint[$i]
                Write-Debug "The following operation failed: Get-BitLockerVolumeInternal -MountPoint $m"
                continue
            }

            Write-Debug ("MountPoint: " + $BitLockerVolumeInternal.MountPoint)

            if ($pscmdlet.ShouldProcess($BitLockerVolumeInternal.MountPoint))
            {
                $Win32EncryptableVolume     =  Get-Win32EncryptableVolumeInternal -MountPoint $BitLockerVolumeInternal.MountPoint
                if ($RebootCount -ne -1)
                {
                    $DisableKeyProtectorsResult =  Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName DisableKeyProtectors -Arguments @{DisableCount = [uint32]$RebootCount}
                }
                else
                {
                    $DisableKeyProtectorsResult =  Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName DisableKeyProtectors
                }

                if ($DisableKeyProtectorsResult.ReturnValue -ne 0)
                {
                    $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $DisableKeyProtectorsResult.ReturnValue
                    Write-Error -Exception $ExceptionForHr
                    continue
                }

                $ValidMountPoint = $ValidMountPoint + $MountPoint[$i]
            }
        } 

        Write-Debug "ValidMountPoint: $ValidMountPoint"

        if ($ValidMountPoint)
        {
            $BitLockerVolume = Get-BitLockerVolume -MountPoint $ValidMountPoint

            $BitLockerVolume
        }
        else
        {
            Write-Debug "No valid mount point was provided that can be suspended"
        }

        Write-Debug "End Suspend-BitLocker. Return $BitLockerVolume"
    }
}

#########################################################################################
# Resume-BitLocker
#
# Returns BitLockerVolume structures that describes the volumes which have been resumed.
# Resumed means that the key protectors have been enabled. The drive contents are still
# encrypted and if encryption or decryption is paused then this cmdlet will not change
# that. To resume the encryption or decryption process you need to call the WMI method 
# ResumeConversion.
#
# Input: String[]    - array of volume names. Could be: drive letter or volume id or mounted directory
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume[]
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function Resume-BitLocker
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
          [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string[]]
          $MountPoint)
    process
    {
       Write-Debug "Begin Resume-BitLocker($MountPoint)"

       <#
       # ValidMountPoint is a subset of the elements of MountPoint array.
       # If MountPoint array contains an element that is not a valid mount point whose protectors
       # can be disabled then the mount point is not part of ValidMountPoint
       # Only those BitLockerVolume structures are returned that are part of ValidMountPoint
       #
       # If "-whatif" is used then ValidMountPoint is always $null
       #>
       [string[]]$ValidMountPoint = $null


       for($i=0; $i -lt $MountPoint.Count; $i++)
       {
            $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint[$i]
            if (!$BitLockerVolumeInternal)
            {
                $m = $MountPoint[$i]
                Write-Debug "The following operation failed: Get-BitLockerVolumeInternal -MountPoint $m"
                continue
            }

            Write-Debug ("MountPoint: " + $BitLockerVolumeInternal.MountPoint)

            if ($pscmdlet.ShouldProcess($BitLockerVolumeInternal.MountPoint))
            {
                $Win32EncryptableVolume     =  Get-Win32EncryptableVolumeInternal -MountPoint $BitLockerVolumeInternal.MountPoint
                $EnableKeyProtectorsResult  =  Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName EnableKeyProtectors

                if ($EnableKeyProtectorsResult.ReturnValue -ne 0)
                {
                    $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $EnableKeyProtectorsResult.ReturnValue
                    Write-Error -Exception $ExceptionForHr
                    continue
                }

                $ValidMountPoint = $ValidMountPoint + $MountPoint[$i]
            }

        } 

        Write-Debug "ValidMountPoint: $ValidMountPoint"

        if ($ValidMountPoint)
        {
            $BitLockerVolume = Get-BitLockerVolume -MountPoint $ValidMountPoint

            $BitLockerVolume
        }
        else
        {
            Write-Debug "No valid mount point was provided that can be resumed"
        }


        Write-Debug "End Resume-BitLocker. Return $BitLockerVolume"
    }
}

#########################################################################################
# Lock-BitLocker
#
# Returns BitLockerVolume structures that describes the volumes which have been locked.
# Locked means volume is dismounted and the volume's encryption key is removed from system memory.
# The contents of the volume remain inacessible until it is unlocked.
#
# Input: String[]      - array of volume names. Could be: drive letter or volume id or mounted directory
#        Bool          - ForceDismount flag. If $true the disk is forcefully dismounted
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume[]
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function Lock-BitLocker
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
          [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string[]]
          $MountPoint,

          [Parameter(Mandatory = $false)]
          [Alias("fd")]
          [System.Management.Automation.SwitchParameter]
          $ForceDismount = $false)

    process
    {
       Write-Debug "Begin Lock-BitLocker($MountPoint)"

       #########
       # ValidMountPoint is a subset of the elements of MountPoint array.
       # If MountPoint array contains an element that is not a valid mount point then
       # the mount point is not part of ValidMountPoint
       # Only those BitLockerVolume structures are returned that are part of ValidMountPoint
       #
       # If "-whatif" is used then ValidMountPoint is always $null
       #########
       [string[]]$ValidMountPoint = $null

       for($i=0; $i -lt $MountPoint.Count; $i++)
       {
            $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint[$i]
            if (!$BitLockerVolumeInternal)
            {
                $m = $MountPoint[$i]
                Write-Debug "The following operation failed: Get-BitLockerVolumeInternal -MountPoint $m"
                continue
            }

            Write-Debug ("MountPoint: " + $BitLockerVolumeInternal.MountPoint)

            if ($pscmdlet.ShouldProcess($BitLockerVolumeInternal.MountPoint))
            {
                $Win32EncryptableVolume  =  Get-Win32EncryptableVolumeInternal -MountPoint $BitLockerVolumeInternal.MountPoint
                $LockResult              =  Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName Lock -Arguments @{ForceDismount = $ForceDismount.IsPresent}

                if ($LockResult.ReturnValue -ne 0)
                {
                    $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $LockResult.ReturnValue
                    Write-Error -Exception $ExceptionForHr
                    continue
                }

                $ValidMountPoint = $ValidMountPoint + $MountPoint[$i]
            }
        } 

        Write-Debug "ValidMountPoint: $ValidMountPoint"

        if ($ValidMountPoint)
        {
            $BitLockerVolume = Get-BitLockerVolume -MountPoint $ValidMountPoint

            $BitLockerVolume
        }
        else
        {
            Write-Debug "No valid mount point was provided that can be locked"
        }

        Write-Debug "End Lock-BitLocker. Return $BitLockerVolume"
    }
}

#########################################################################################
# Unlock-PasswordInternal
#
# Returns BitLockerVolume structure that describes the volume after it was unlocked
#
# Input: String           - volume name. Could be: drive letter or volume id or mounted directory
#        SecureString     - password to use for unlocking
#
# Return: 0 for success
#########################################################################################
function Unlock-PasswordInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $true)]
          [System.Security.SecureString]
          $Password
    )
    process
    {
        Write-Debug "Begin Unlock-PasswordInternal"

        #
        # Convert secure string to cleartext.
        #

        $ClearTextPassword = Decrypt-SecureStringInternal $Password

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint
        $UnlockResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName UnlockWithPassphrase -Arguments @{PassPhrase = $ClearTextPassword}

        # Clear the clear text password string
        $ClearTextPassword = ""

        if ($UnlockResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $UnlockResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $UnlockResult.ReturnValue
        }

        Write-Debug "End Unlock-PasswordInternal"

        return 0
    }
}

#########################################################################################
# Unlock-RecoveryPasswordInternal
#
# Returns BitLockerVolume structure that describes the volume after it was unlocked
#
# Input: String           - volume name. Could be: drive letter or volume id or mounted directory
#        String           - recovery password to use for unlocking
#
# Return: 0 for success
#########################################################################################
function Unlock-RecoveryPasswordInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $true)]
          [string]
          $RecoveryPassword
    )
    process
    {
        Write-Debug "Begin Unlock-RecoveryPasswordInternal"

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint
        $UnlockResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName UnlockWithNumericalPassword -Arguments @{NumericalPassword = $RecoveryPassword}

        if ($UnlockResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $UnlockResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $UnlockResult.ReturnValue
        }

        Write-Debug "End Unlock-RecoveryPasswordInternal"

        return 0
    }
}

#########################################################################################
# Unlock-RecoveryKeyInternal
#
# Returns BitLockerVolume structure that describes the volume after it was unlocked
#
# Input: String           - volume name. Could be: drive letter or volume id or mounted directory
#        String           - recovery key to use for unlocking
#
# Return: 0 for success
#########################################################################################
function Unlock-RecoveryKeyInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $true)]
          [string]
          $RecoveryKeyPath
    )
    process
    {
        Write-Debug "Begin Unlock-RecoveryKeyInternal"

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint

        $GetExternalKeyFromFileResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetExternalKeyFromFile -Arguments @{PathWithFileName = $RecoveryKeyPath}

        if ($GetExternalKeyFromFileResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $GetExternalKeyFromFileResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $GetExternalKeyFromFileResult.ReturnValue
        }

        $UnlockResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName UnlockWithExternalKey -Arguments @{ExternalKey = $GetExternalKeyFromFileResult.ExternalKey}

        if ($UnlockResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $UnlockResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $UnlockResult.ReturnValue
        }

        Write-Debug "End Unlock-RecoveryKeyInternal"

        return 0
    }
}

#########################################################################################
# Unlock-BitLocker
#
# Returns BitLockerVolume structures that describes the volumes which have been unlocked.
#
# Input: String[]      - array of volume names. Could be: drive letter or volume id or mounted directory
#        Protector     - protector to use for unlocking
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume[]
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function Unlock-BitLocker
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
          [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string[]]
          $MountPoint,

          #
          # Password Protector
          #

          [Parameter(Mandatory = $true, ParameterSetName="OnlyPasswordParameterSet")]
          [Alias("pw")]
          [System.Security.SecureString]
          $Password,

          #
          # Recovery Password Protector
          #

          [Parameter(Mandatory = $true, ParameterSetName="OnlyRecoveryPasswordParameterSet")]
          [ValidateNotNullOrEmpty()]
          [Alias("rp")]
          [String]
          $RecoveryPassword,

          #
          # Recovery Key Protector
          #

          [Parameter(Mandatory = $true, ParameterSetName="OnlyRecoveryKeyParameterSet")]
          [ValidateNotNullOrEmpty()]
          [Alias("rk")]
          [String]
          $RecoveryKeyPath,

          #
          # Ad Account Or Group Protector
          #

          [Parameter(Mandatory = $true, ParameterSetName="OnlyAdAccountOrGroupParameterSet")]
          [System.Management.Automation.SwitchParameter]
          $AdAccountOrGroup
    )

    process
    {
       Write-Debug "Begin Unlock-BitLocker($MountPoint)"

       #########
       # ValidMountPoint is a subset of the elements of MountPoint array.
       # If MountPoint array contains an element that is not a valid mount point then
       # the mount point is not part of ValidMountPoint
       # Only those BitLockerVolume structures are returned that are part of ValidMountPoint
       #
       # If "-whatif" is used then ValidMountPoint is always $null
       #########
       [string[]]$ValidMountPoint = $null

       for($i=0; $i -lt $MountPoint.Count; $i++)
       {
            $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint[$i]
            if (!$BitLockerVolumeInternal)
            {
                $m = $MountPoint[$i]
                Write-Debug "The following operation failed: Get-BitLockerVolumeInternal -MountPoint $m"
                continue
            }

            Write-Debug ("MountPoint: " + $BitLockerVolumeInternal.MountPoint)

            if ($pscmdlet.ShouldProcess($BitLockerVolumeInternal.MountPoint))
            {
                if ($PsCmdlet.ParameterSetName -eq "OnlyPasswordParameterSet")
                {
                    $Result = Unlock-PasswordInternal $BitLockerVolumeInternal.MountPoint $Password
                }
                elseif ($PsCmdlet.ParameterSetName -eq "OnlyRecoveryPasswordParameterSet")
                {
                    $Result = Unlock-RecoveryPasswordInternal $BitLockerVolumeInternal.MountPoint $RecoveryPassword
                }
                elseif ($PsCmdlet.ParameterSetName -eq "OnlyRecoveryKeyParameterSet")
                {
                    $Result = Unlock-RecoveryKeyInternal $BitLockerVolumeInternal.MountPoint $RecoveryKeyPath
                }
                elseif ($PsCmdlet.ParameterSetName -eq "OnlyAdAccountOrGroupParameterSet")
                {
                    $Result = Unlock-AdAccountOrGroupInternal $BitLockerVolumeInternal.MountPoint
                }

                if ($Result -ne 0)
                {
                    Write-Debug "Unlock-BitLocker failed for $BitLockerVolumeInternal.MountPoint"
                    continue
                }

                $ValidMountPoint = $ValidMountPoint + $MountPoint[$i]
            }
        } 

        Write-Debug "ValidMountPoint: $ValidMountPoint"

        if ($ValidMountPoint)
        {
            $BitLockerVolume = Get-BitLockerVolume -MountPoint $ValidMountPoint

            $BitLockerVolume
        }
        else
        {
            Write-Debug "No valid mount point was provided that can be unlocked"
        }

        Write-Debug "End Unlock-BitLocker. Return $BitLockerVolume"
    }
}

#########################################################################################
# Add-RecoveryPasswordProtectorInternal
#
# Returns BitLockerVolume structure that describes the volume with the new recovery password protector
#
# Input: String           - volume name. Could be: drive letter or volume id or mounted directory
#        RecoveryPassword - recovery password protector to add. Can be empty.
#
# Return: 0 for success
#########################################################################################
function Add-RecoveryPasswordProtectorInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $false)]
          [string]
          $RecoveryPassword,

          [Parameter(Mandatory = $false)]
          [System.Management.Automation.SwitchParameter]
          $SuppressWarningMessage = $false
    )
    process
    {
        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint

        if ($RecoveryPassword -eq "")
        {
            $AddKeyProtectorResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName ProtectKeyWithNumericalPassword -Arguments @{FriendlyName = $null; NumericalPassword = $null}
        }
        else
        {
            $AddKeyProtectorResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName ProtectKeyWithNumericalPassword -Arguments @{FriendlyName = $null; NumericalPassword = $RecoveryPassword}
        }

        if ($AddKeyProtectorResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $AddKeyProtectorResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $AddKeyProtectorResult.ReturnValue
        }

        if (!$SuppressWarningMessage)
        {
            $KeyProtectorId = $AddKeyProtectorResult.VolumeKeyProtectorID

            $RecoveryPasswordResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetKeyProtectorNumericalPassword -Arguments @{VolumeKeyProtectorID = $KeyProtectorId}
            if ($RecoveryPasswordResult.ReturnValue -ne 0)
            {
                $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $RecoveryPasswordResult.ReturnValue
                Write-Error -Exception $ExceptionForHr
                return $RecoveryPasswordResult.ReturnValue
            }
            $RecoveryPassword = $RecoveryPasswordResult.NumericalPassword
            Write-Debug "RecoveryPassword added"

            $Message = [string]::Format($stringTable.WarningWriteDownRecoveryPassword, $RecoveryPassword, [Environment]::NewLine)
            Write-Warning $Message
        }

        return 0
    }
}

#########################################################################################
# Add-PasswordProtectorInternal
#
# Adds a passphrase protector to a volume
#
# Input: String           - volume name. Could be: drive letter or volume id or mounted directory
#        SecureString     - password
#
# Return: 0 for success
#########################################################################################
function Add-PasswordProtectorInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $false)]
          [System.Security.SecureString]
          $Password
    )
    process
    {
        if ($Password -eq $null)
        {
            $Password = Read-UserSecretInternal -Message $stringTable.PasswordPrompt -ConfirmMessage $stringTable.ConfirmPasswordPrompt -NotMatchMessage $stringTable.NoMatchPassword
        }

        #
        # Convert secure string to cleartext.
        #
        $ClearTextPassword = Decrypt-SecureStringInternal $Password

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint
        $AddKeyProtectorResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName ProtectKeyWithPassphrase -Arguments @{FriendlyName = $null; PassPhrase = $ClearTextPassword}

        # Clear the clear text password string
        $ClearTextPassword = ""

        if ($AddKeyProtectorResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $AddKeyProtectorResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $AddKeyProtectorResult.ReturnValue
        }

        return 0
    }
}

#########################################################################################
# Remove-KeyProtectorByTypeInternal
#
# Deletes one key protector of the type specified by $ProtectorType
#
# Input: String - volume name. Could be: drive letter or volume id or mounted directory
#        uint32 - protector type of protector that is to be deleted
#
# Return: 0 for success
#########################################################################################
function Remove-KeyProtectorByTypeInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $true)]
          [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]
          $ProtectorType
    )
    process
    {
        #
        # Get key protectors of type $ProtectorType
        #

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint
        $KeyProtectorIdsResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetKeyProtectors -Arguments @{KeyProtectorType = [uint32]$ProtectorType}

        if ($KeyProtectorIdsResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $KeyProtectorIdsResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $KeyProtectorIdsResult.ReturnValue
        }
        if ($KeyProtectorIdsResult.VolumeKeyProtectorID.Count -ne 1)
        {
            #
            # Return success
            #

            return 0
        }

        $Result = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName DeleteKeyProtector -Arguments @{VolumeKeyProtectorID = $KeyProtectorIdsResult.VolumeKeyProtectorID[0]}
        if ($Result.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $Result.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $Result.ReturnValue
        }

        return 0
    }
}

#########################################################################################
# Add-TpmProtectorInternal
#
# Adds a TPM protector to a volume
#
# Input: String           - volume name. Could be: drive letter or volume id or mounted directory
#
# Return: 0 for success
#########################################################################################
function Add-TpmProtectorInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint
    )
    process
    {
        Write-Debug "Begin Add-TpmProtectorInternal"

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint
        $AddKeyProtectorResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName ProtectKeyWithTPM -Arguments @{FriendlyName = $null; PlatformValidationProfile = $null}

        if ($AddKeyProtectorResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $AddKeyProtectorResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $AddKeyProtectorResult.ReturnValue
        }

        #
        # Delete all other TPM based protectors
        #

        $Result = Remove-KeyProtectorByTypeInternal $MountPoint TpmPin
        if ($Result -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $Result
            Write-Error -Exception $ExceptionForHr
            return $Result.ReturnValue
        }

        $Result = Remove-KeyProtectorByTypeInternal $MountPoint TpmStartupKey
        if ($Result -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $Result
            Write-Error -Exception $ExceptionForHr
            return $Result.ReturnValue
        }

        $Result = Remove-KeyProtectorByTypeInternal $MountPoint TpmPinStartupKey
        if ($Result -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $Result
            Write-Error -Exception $ExceptionForHr
            return $Result.ReturnValue
        }

        Write-Debug "End Add-TpmProtectorInternal"

        return 0
    }
}

#########################################################################################
# Add-TpmAndPinProtectorInternal
#
# Adds a TPMAndPin protector to a volume
#
# Input: String           - volume name. Could be: drive letter or volume id or mounted directory
#        SecureString     - Pin
#
# Return: 0 for success
#########################################################################################
function Add-TpmAndPinProtectorInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $false)]
          [System.Security.SecureString]
          $Pin
    )
    process
    {
        Write-Debug "Begin Add-TpmAndPinProtectorInternal"

        if ($Pin -eq $null)
        {
            $Pin = Read-UserSecretInternal -Message $stringTable.PinPrompt -ConfirmMessage $stringTable.ConfirmPinPrompt -NotMatchMessage $stringTable.NoMatchPin
        }

        #
        # Convert secure string to cleartext.
        #
        $ClearTextPin = Decrypt-SecureStringInternal $Pin

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint
        $AddKeyProtectorResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName ProtectKeyWithTPMAndPin -Arguments @{FriendlyName = $null; PlatformValidationProfile = $null; PIN = $ClearTextPin}

        # Clear the clear text pin
        $ClearTextPin = ""

        if ($AddKeyProtectorResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $AddKeyProtectorResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $AddKeyProtectorResult.ReturnValue
        }

        #
        # Delete all other TPM based protectors
        #

        $Result = Remove-KeyProtectorByTypeInternal $MountPoint Tpm
        if ($Result -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $Result
            Write-Error -Exception $ExceptionForHr
            return $Result.ReturnValue
        }

        $Result = Remove-KeyProtectorByTypeInternal $MountPoint TpmStartupKey
        if ($Result -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $Result
            Write-Error -Exception $ExceptionForHr
            return $Result.ReturnValue
        }

        $Result = Remove-KeyProtectorByTypeInternal $MountPoint TpmPinStartupKey
        if ($Result -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $Result
            Write-Error -Exception $ExceptionForHr
            return $Result.ReturnValue
        }

        Write-Debug "End Add-TpmAndPinProtectorInternal"

        return 0
    }
}

#########################################################################################
# Add-TpmAndStartupKeyProtectorInternal
#
# Adds a TPMAndStartupKey protector to a volume
#
# Input: String           - volume name. Could be: drive letter or volume id or mounted directory
#        String           - Startup Key path
#
# Return: 0 for success
#########################################################################################
function Add-TpmAndStartupKeyProtectorInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [String]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $true)]
          [String]
          $StartupKeyPath
    )
    process
    {
        Write-Debug "Begin Add-TpmAndStartupKeyProtectorInternal"

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint
        $AddKeyProtectorResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName ProtectKeyWithTPMAndStartupKey -Arguments @{ExternalKey = $null; FriendlyName = $null; PlatformValidationProfile = $null}

        if ($AddKeyProtectorResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $AddKeyProtectorResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $AddKeyProtectorResult.ReturnValue
        }

        $SaveExternalKeyResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName SaveExternalKeyToFile -Arguments @{VolumeKeyProtectorID = $AddKeyProtectorResult.VolumeKeyProtectorId; Path = $StartupKeyPath}
        if ($SaveExternalKeyResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $SaveExternalKeyResult.ReturnValue
            Write-Error -Exception $ExceptionForHr

            #
            # Remove previously added protector
            #

            $r = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName DeleteKeyProtector -Arguments @{VolumeKeyProtectorID = $AddKeyProtectorResult.VolumeKeyProtectorID}

            return $SaveExternalKeyResult.ReturnValue
        }

        #
        # Delete all other TPM based protectors
        #

        $Result = Remove-KeyProtectorByTypeInternal $MountPoint Tpm
        if ($Result -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $Result
            Write-Error -Exception $ExceptionForHr
            return $Result.ReturnValue
        }

        $Result = Remove-KeyProtectorByTypeInternal $MountPoint TpmPin
        if ($Result -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $Result
            Write-Error -Exception $ExceptionForHr
            return $Result.ReturnValue
        }

        $Result = Remove-KeyProtectorByTypeInternal $MountPoint TpmPinStartupKey
        if ($Result -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $Result
            Write-Error -Exception $ExceptionForHr
            return $Result.ReturnValue
        }

        Write-Debug "End Add-TpmAndStartupKeyProtectorInternal"

        return 0
    }
}

#########################################################################################
# Add-TpmAndPinAndStartupKeyProtectorInternal
#
# Adds a TPMAndPinAndStartupKey protector to a volume
#
# Input: String           - volume name. Could be: drive letter or volume id or mounted directory
#        SecureString     - Pin
#        String           - Startup Key path
#
# Return: 0 for success
#########################################################################################
function Add-TpmAndPinAndStartupKeyProtectorInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [String]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $true)]
          [String]
          $StartupKeyPath,

          [Parameter(Position = 2, Mandatory = $false)]
          [System.Security.SecureString]
          $Pin
    )
    process
    {
        Write-Debug "Begin Add-TpmAndPinAndStartupKeyProtectorInternal"

        if ($Pin -eq $null)
        {
            $Pin = Read-UserSecretInternal -Message $stringTable.PinPrompt -ConfirmMessage $stringTable.ConfirmPinPrompt -NotMatchMessage $stringTable.NoMatchPin
        }

        #
        # Convert secure string to cleartext.
        #
        $ClearTextPin = Decrypt-SecureStringInternal $Pin

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint
        $AddKeyProtectorResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName ProtectKeyWithTPMAndPinAndStartupKey -Arguments @{ExternalKey = $null; FriendlyName = $null; PIN = $ClearTextPin; PlatformValidationProfile = $null}

        # Clear the clear text pin
        $ClearTextPin = ""

        if ($AddKeyProtectorResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $AddKeyProtectorResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $AddKeyProtectorResult.ReturnValue
        }

        $SaveExternalKeyResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName SaveExternalKeyToFile -Arguments @{VolumeKeyProtectorID = $AddKeyProtectorResult.VolumeKeyProtectorId; Path = $StartupKeyPath}
        if ($SaveExternalKeyResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $SaveExternalKeyResult.ReturnValue
            Write-Error -Exception $ExceptionForHr

            #
            # Remove previously added protector
            #

            $r = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName DeleteKeyProtector -Arguments @{VolumeKeyProtectorID = $AddKeyProtectorResult.VolumeKeyProtectorID}

            return $SaveExternalKeyResult.ReturnValue
        }

        #
        # Delete all other TPM based protectors
        #

        $Result = Remove-KeyProtectorByTypeInternal $MountPoint Tpm
        if ($Result -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $Result
            Write-Error -Exception $ExceptionForHr
            return $Result.ReturnValue
        }

        $Result = Remove-KeyProtectorByTypeInternal $MountPoint TpmPin
        if ($Result -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $Result
            Write-Error -Exception $ExceptionForHr
            return $Result.ReturnValue
        }

        $Result = Remove-KeyProtectorByTypeInternal $MountPoint TpmStartupKey
        if ($Result -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $Result
            Write-Error -Exception $ExceptionForHr
            return $Result.ReturnValue
        }

        Write-Debug "End Add-TpmAndPinAndStartupKeyProtectorInternal"

        return 0
    }
}

#########################################################################################
# Add-ExternalKeyProtectorInternal
#
# Adds an ExternalKey protector to a volume (Startup Key, Recovery Key)
#
# Input: String           - volume name. Could be: drive letter or volume id or mounted directory
#        String           - External Key path
#
# Return: 0 for success
#########################################################################################
function Add-ExternalKeyProtectorInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [String]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $true)]
          [String]
          $ExternalKeyPath
    )
    process
    {
        Write-Debug "Begin Add-ExternalKeyProtectorInternal"

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint
        $AddKeyProtectorResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName ProtectKeyWithExternalKey -Arguments @{ExternalKey = $null; FriendlyName = $null}

        if ($AddKeyProtectorResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $AddKeyProtectorResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $AddKeyProtectorResult.ReturnValue
        }

        $SaveExternalKeyResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName SaveExternalKeyToFile -Arguments @{VolumeKeyProtectorID = $AddKeyProtectorResult.VolumeKeyProtectorId; Path = $ExternalKeyPath}
        if ($SaveExternalKeyResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $SaveExternalKeyResult.ReturnValue
            Write-Error -Exception $ExceptionForHr

            #
            # Remove previously added protector
            #

            $r = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName DeleteKeyProtector -Arguments @{VolumeKeyProtectorID = $AddKeyProtectorResult.VolumeKeyProtectorID}

            return $SaveExternalKeyResult.ReturnValue
        }

        Write-Debug "End Add-ExternalKeyProtectorInternal"

        return 0
    }
}

#########################################################################################
# Add-SidProtectorInternal
#
# Adds a SID protector to a volume
#
# Input: String           - volume name. Could be: drive letter or volume id or mounted directory
#        String           - SID
#
# Return: 0 for success
#########################################################################################
function Add-SidProtectorInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [String]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $true)]
          [String]
          $Sid,

          [Parameter(Position = 2, Mandatory = $true)]
          [bool]
          $Service
    )
    process
    {
        Write-Debug "Begin Add-SidProtectorInternal"

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint

        $flags = 0
        if ($Service -eq $true)
        {
            $flags = 1 # 1 means FVE_DPAPI_NG_FLAG_UNLOCK_AS_SERVICE_ACCOUNT
        }

        $User = [System.Security.Principal.NTAccount]($Sid)

        try
        {
            $SidStr = $User.Translate([System.Security.Principal.SecurityIdentifier])
        }
        catch
        {
            #
            # Failed to translate, so try to use what the user gave us
            #

            try
            {
                $SidStr = [System.Security.Principal.SecurityIdentifier]($Sid)
            }
            catch
            {
                Write-Error -Exception $_.Exception
                return 1
            }
        }

        $AddKeyProtectorResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName ProtectKeyWithAdSid -Arguments @{FriendlyName = $null; SidString = $SidStr.Value; Flags = [uint32]$flags}

        if ($AddKeyProtectorResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $AddKeyProtectorResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $AddKeyProtectorResult.ReturnValue
        }

        Write-Debug "End Add-SidProtectorInternal"

        return 0
    }
}

#########################################################################################
# Add-BitLockerKeyProtector
#
# Returns BitLockerVolume structures that describe the volumes with the new protector.
#
# Input: String[]      - array of volume names. Could be: drive letter or volume id or mounted directory
#        KeyProtector  - key protector to add
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume[]
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function Add-BitLockerKeyProtector
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
          [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string[]]
          $MountPoint,

          # 
          # Password Protector
          #

          [Parameter(Mandatory = $true, ParameterSetName="PasswordProtector")]
          [Alias("pwp")]
          [System.Management.Automation.SwitchParameter]
          $PasswordProtector,

          [Parameter(Mandatory = $false, ParameterSetName="PasswordProtector", Position = 1)]
          [Alias("pw")]
          [System.Security.SecureString]
          $Password,

          # 
          # Recovery Password Protector
          #

          [Parameter(Mandatory = $true, ParameterSetName="RecoveryPasswordProtector")]
          [Alias("rpp")]
          [System.Management.Automation.SwitchParameter]
          $RecoveryPasswordProtector,

          [Parameter(Mandatory = $false, ParameterSetName="RecoveryPasswordProtector", Position = 1)]
          [ValidateNotNullOrEmpty()]
          [Alias("rp")]
          [String]
          $RecoveryPassword,

          # 
          # Startup Key Protector
          #

          [Parameter(Mandatory = $true, ParameterSetName="StartupKeyProtector")]
          [Alias("skp")]
          [System.Management.Automation.SwitchParameter]
          $StartupKeyProtector,

          [Parameter(Mandatory = $true, ParameterSetName="StartupKeyProtector", Position = 1)]
          [Parameter(Mandatory = $true, ParameterSetName="TpmAndPinAndStartupKeyProtector", Position = 1)]
          [Parameter(Mandatory = $true, ParameterSetName="TpmAndStartupKeyProtector", Position = 1)]
          [Alias("sk")]
          [String]
          $StartupKeyPath,

          # 
          # Sid Protector
          #

          [Parameter(Mandatory = $true, ParameterSetName="SidProtector")]
          [Alias("sidp")]
          [System.Management.Automation.SwitchParameter]
          $ADAccountOrGroupProtector,

          [Parameter(Mandatory = $true, ParameterSetName="SidProtector", Position = 1)]
          [Alias("sid")]
          [String]
          $ADAccountOrGroup,

          [Parameter(Mandatory = $false, ParameterSetName="SidProtector")]
          [System.Management.Automation.SwitchParameter]
          $Service,

          # 
          # TPM And Pin And StartupKey Protector
          #

          [Parameter(Mandatory = $true, ParameterSetName="TpmAndPinAndStartupKeyProtector")]
          [Alias("tpskp")]
          [System.Management.Automation.SwitchParameter]
          $TpmAndPinAndStartupKeyProtector,

# Defined in the StartupKeyProtector section above
#          [Parameter(Mandatory = $true, ParameterSetName="TpmAndPinAndStartupKeyProtector", Position = 1)]
#          [String]
#          $StartupKeyPath,

          [Parameter(Mandatory = $false, ParameterSetName="TpmAndPinAndStartupKeyProtector", Position = 2)]
          [Parameter(Mandatory = $false, ParameterSetName="TpmAndPinProtector", Position = 1)]
          [Alias("p")]
          [System.Security.SecureString]
          $Pin,


          # 
          # TPM And Pin Protector
          #

          [Parameter(Mandatory = $true, ParameterSetName="TpmAndPinProtector")]
          [Alias("tpp")]
          [System.Management.Automation.SwitchParameter]
          $TpmAndPinProtector,

# Defined in TPM And Pin And Startup Key Protector section above
#          [Parameter(Mandatory = $false, ParameterSetName="TpmAndPinProtector", Position = 1)]
#          [System.Security.SecureString]
#          $Pin,


          # 
          # TPM And StartupKey Protector
          #

          [Parameter(Mandatory = $true, ParameterSetName="TpmAndStartupKeyProtector")]
          [Alias("tskp")]
          [System.Management.Automation.SwitchParameter]
          $TpmAndStartupKeyProtector,

# Defined in the StartupKeyProtector section above
#          [Parameter(Mandatory = $true, ParameterSetName="TpmAndStartupKeyProtector", Position = 1)]
#          [String]
#          $StartupKeyPath,

          # 
          # TPM Protector
          #

          [Parameter(Mandatory = $true, ParameterSetName="TpmProtector")]
          [Alias("tpmp")]
          [System.Management.Automation.SwitchParameter]
          $TpmProtector,

          # 
          # Recovery Key Protector
          #

          [Parameter(Mandatory = $true, ParameterSetName="RecoveryKeyProtector")]
          [Alias("rkp")]
          [System.Management.Automation.SwitchParameter]
          $RecoveryKeyProtector,

          [Parameter(Mandatory = $true, ParameterSetName="RecoveryKeyProtector", Position = 1)]
          [Alias("rk")]
          [String]
          $RecoveryKeyPath
    )
    process
    {
        Write-Debug "Begin Add-BitLockerKeyProtector"

       #########
       # ValidMountPoint is a subset of the elements of MountPoint array.
       # If MountPoint array contains an element that is not a valid mount point then
       # the mount point is not part of ValidMountPoint
       # Only those BitLockerVolume structures are returned that are part of ValidMountPoint
       #
       # If "-whatif" is used then ValidMountPoint is always $null
       #########
       [string[]]$ValidMountPoint = $null

       for($i=0; $i -lt $MountPoint.Count; $i++)
       {
            $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint[$i]
            if (!$BitLockerVolumeInternal)
            {
                $m = $MountPoint[$i]
                Write-Debug "The following operation failed: Get-BitLockerVolumeInternal -MountPoint $m"
                continue
            }

            Write-Debug ("MountPoint: " + $BitLockerVolumeInternal.MountPoint)

            if ($pscmdlet.ShouldProcess($BitLockerVolumeInternal.MountPoint))
            {
                if ($PsCmdlet.ParameterSetName -eq "RecoveryPasswordProtector")
                {
                    $Result = Add-RecoveryPasswordProtectorInternal $BitLockerVolumeInternal.MountPoint $RecoveryPassword
                }
                elseif ($PsCmdlet.ParameterSetName -eq "PasswordProtector")
                {
                    $Result = Add-PasswordProtectorInternal $BitLockerVolumeInternal.MountPoint $Password
                }
                elseif ($PsCmdlet.ParameterSetName -eq "TpmProtector")
                {
                    $Result = Add-TpmProtectorInternal $BitLockerVolumeInternal.MountPoint
                }
                elseif ($PsCmdlet.ParameterSetName -eq "TpmAndPinProtector")
                {
                    $Result = Add-TpmAndPinProtectorInternal $BitLockerVolumeInternal.MountPoint $Pin
                }
                elseif ($PsCmdlet.ParameterSetName -eq "TpmAndStartupKeyProtector")
                {
                    $Result = Add-TpmAndStartupKeyProtectorInternal $BitLockerVolumeInternal.MountPoint $StartupKeyPath
                }
                elseif ($PsCmdlet.ParameterSetName -eq "TpmAndPinAndStartupKeyProtector")
                {
                    $Result = Add-TpmAndPinAndStartupKeyProtectorInternal $BitLockerVolumeInternal.MountPoint $StartupKeyPath $Pin
                }
                elseif ($PsCmdlet.ParameterSetName -eq "StartupKeyProtector")
                {
                    $Result = Add-ExternalKeyProtectorInternal $BitLockerVolumeInternal.MountPoint $StartupKeyPath
                }
                elseif ($PsCmdlet.ParameterSetName -eq "RecoveryKeyProtector")
                {
                    $Result = Add-ExternalKeyProtectorInternal $BitLockerVolumeInternal.MountPoint $RecoveryKeyPath
                }
                elseif ($PsCmdlet.ParameterSetName -eq "SidProtector")
                {
                    $Result = Add-SidProtectorInternal $BitLockerVolumeInternal.MountPoint $ADAccountOrGroup $Service
                }

                if ($Result -ne 0)
                {
                    Write-Debug "Add-BitLockerKeyProtector failed for $BitLockerVolumeInternal.MountPoint"
                    continue
                }
                
                $ValidMountPoint = $ValidMountPoint + $MountPoint[$i]
            }
        } 

        Write-Debug "ValidMountPoint: $ValidMountPoint"

        if ($ValidMountPoint)
        {
            $BitLockerVolume = Get-BitLockerVolume -MountPoint $ValidMountPoint

            $BitLockerVolume
        }
        else
        {
            Write-Debug "No valid mount point was provided"
        }

       Write-Debug "End Add-BitLockerKeyProtector"
    }
}

#########################################################################################
# Remove-BitLockerKeyProtector
#
# Returns BitLockerVolume structures that describe the volumes after the protector is deleted.
#
# Input: String[]      - array of volume names. Could be: drive letter or volume id or mounted directory
#        String        - ID of key protector to be removed
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume[]
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function Remove-BitLockerKeyProtector
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
          [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string[]]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [Alias("id")]
          [string]
          $KeyProtectorId
    )
    process
    {
       Write-Debug "Begin Remove-BitLockerKeyProtector($MountPoint, $KeyProtectorId)"

       #########
       # ValidMountPoint is a subset of the elements of MountPoint array.
       # If MountPoint array contains an element that is not a valid mount point then
       # the mount point is not part of ValidMountPoint
       # Only those BitLockerVolume structures are returned that are part of ValidMountPoint
       #
       # If "-whatif" is used then ValidMountPoint is always $null
       #########
       [string[]]$ValidMountPoint = $null

       for($i=0; $i -lt $MountPoint.Count; $i++)
       {
            $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint[$i]
            if (!$BitLockerVolumeInternal)
            {
                $m = $MountPoint[$i]
                Write-Debug "The following operation failed: Get-BitLockerVolumeInternal -MountPoint $m"
                continue
            }

            Write-Debug ("MountPoint: " + $BitLockerVolumeInternal.MountPoint)

            if ($pscmdlet.ShouldProcess($BitLockerVolumeInternal.MountPoint))
            {
                $Win32EncryptableVolume   = Get-Win32EncryptableVolumeInternal -MountPoint $BitLockerVolumeInternal.MountPoint

                $DraKeyProtector = $BitLockerVolumeInternal.KeyProtector | 
                                        where {$_.KeyProtectorId -eq $KeyProtectorId -and
                                               $_.KeyProtectorType -eq [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::PublicKey -and 
                                               [uint32]$_.KeyCertificateType -band [uint32][Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorCertificateTypes]::DataRecoveryAgent -eq [uint32][Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorCertificateTypes]::DataRecoveryAgent}

                if ($DraKeyProtector -ne $null)
                {
                    Write-Error -Message $stringTable.ErrorRemoveDraProtector
                    continue
                }

                $NkpProtector = $BitLockerVolumeInternal.KeyProtector |
                                        where {$_.KeyProtectorId -eq $KeyProtectorId -and
                                               $_.KeyProtectorType -eq [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::TpmNetworkKey}

                if ($NkpProtector -ne $null)
                {
                    Write-Error -Message $stringTable.ErrorRemoveNkpProtector
                    continue
                }

                $DeleteKeyProtectorResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName DeleteKeyProtector -Arguments @{VolumeKeyProtectorID = $KeyProtectorId}

                if ($DeleteKeyProtectorResult.ReturnValue -eq $E_KEYREQUIRED)
                {
                    $DisableKeyProtectorsResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName DisableKeyProtectors
                    if ($DisableKeyProtectorsResult.ReturnValue -ne 0)
                    {
                        $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $DisableKeyProtectorsResult
                        Write-Error -Exception $ExceptionForHr
                        continue
                    }

                    $DeleteKeyProtectorResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName DeleteKeyProtector -Arguments @{VolumeKeyProtectorID = $KeyProtectorId}
                }

                if ($DeleteKeyProtectorResult.ReturnValue -ne 0)
                {
                    $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $DeleteKeyProtectorResult.ReturnValue
                    if ($DeleteKeyProtectorResult.ReturnValue -eq $E_VOLUMEBOUND)
                    {
                        $ErrorMessage = [string]::Format($stringTable.ErrorVolumeBoundAlready)
                        Write-Error -Exception $ExceptionForHr -Message $ErrorMessage
                    }
                    else
                    {
                        Write-Error -Exception $ExceptionForHr
                    }
                    continue
                }

                $ValidMountPoint = $ValidMountPoint + $MountPoint[$i]
            }
        } 

        Write-Debug "ValidMountPoint: $ValidMountPoint"

        if ($ValidMountPoint)
        {
            $BitLockerVolume = Get-BitLockerVolume -MountPoint $ValidMountPoint

            $BitLockerVolume
        }
        else
        {
            Write-Debug "No valid combination of mountpoint / protector id found"
        }

        Write-Debug "End Remove-BitLockerKeyProtector. Return $BitLockerVolume"
    }
}

#########################################################################################
# Backup-BitLockerKeyProtector
#
# Returns BitLockerVolume structures that describe the volumes after the protector is backed up.
#
# Input: String[]      - array of volume names. Could be: drive letter or volume id or mounted directory
#        String        - ID of key protector to be backed up
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume[]
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function Backup-BitLockerKeyProtector
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
          [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string[]]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $KeyProtectorId
    )
    process
    {
       Write-Debug "Begin Backup-BitLockerKeyProtector($MountPoint, $KeyProtectorId)"

       #########
       # ValidMountPoint is a subset of the elements of MountPoint array.
       # If MountPoint array contains an element that is not a valid mount point then
       # the mount point is not part of ValidMountPoint
       # Only those BitLockerVolume structures are returned that are part of ValidMountPoint
       #
       # If "-whatif" is used then ValidMountPoint is always $null
       #########
       [string[]]$ValidMountPoint = $null

       for($i=0; $i -lt $MountPoint.Count; $i++)
       {
            $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint[$i]
            if (!$BitLockerVolumeInternal)
            {
                $m = $MountPoint[$i]
                Write-Debug "The following operation failed: Get-BitLockerVolumeInternal -MountPoint $m"
                continue
            }

            Write-Debug ("MountPoint: " + $BitLockerVolumeInternal.MountPoint)

            if ($pscmdlet.ShouldProcess($BitLockerVolumeInternal.MountPoint))
            {
                $Win32EncryptableVolume   = Get-Win32EncryptableVolumeInternal -MountPoint $BitLockerVolumeInternal.MountPoint
                $BackupKeyProtectorResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName BackupRecoveryInformationToActiveDirectory -Arguments @{VolumeKeyProtectorID = $KeyProtectorId}

                if ($BackupKeyProtectorResult.ReturnValue -eq $S_FALSE)
                {
                    $ErrorMessage = [string]::Format($stringTable.ErrorGroupPolicyDisabledBackup)
                    Write-Error -Message $ErrorMessage
                    continue
                }
                elseif ($BackupKeyProtectorResult.ReturnValue -ne 0)
                {
                    $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $BackupKeyProtectorResult.ReturnValue
                    Write-Error -Exception $ExceptionForHr
                    continue
                }

                $ValidMountPoint = $ValidMountPoint + $MountPoint[$i]
            }
        } 

        Write-Debug "ValidMountPoint: $ValidMountPoint"

        if ($ValidMountPoint)
        {
            $BitLockerVolume = Get-BitLockerVolume -MountPoint $ValidMountPoint

            $BitLockerVolume
        }
        else
        {
            Write-Debug "No valid combination of mountpoint / protector id found"
        }

        Write-Debug "End Backup-BitLockerKeyProtector. Return $BitLockerVolume"
    }
}

#########################################################################################
# BackupToAAD-BitLockerKeyProtector
#
# Returns BitLockerVolume structures that describe the volumes after the protector is backed up.
#
# Input: String[]      - array of volume names. Could be: drive letter or volume id or mounted directory
#        String        - ID of key protector to be backed up
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume[]
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function BackupToAAD-BitLockerKeyProtector
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
          [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string[]]
          $MountPoint,

          [Parameter(Position = 1, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $KeyProtectorId
    )
    process
    {
       Write-Debug "Begin BackupToAAD-BitLockerKeyProtector($MountPoint, $KeyProtectorId)"

       #########
       # ValidMountPoint is a subset of the elements of MountPoint array.
       # If MountPoint array contains an element that is not a valid mount point then
       # the mount point is not part of ValidMountPoint
       # Only those BitLockerVolume structures are returned that are part of ValidMountPoint
       #
       # If "-whatif" is used then ValidMountPoint is always $null
       #########
       [string[]]$ValidMountPoint = $null

       for($i=0; $i -lt $MountPoint.Count; $i++)
       {
            $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint[$i]
            if (!$BitLockerVolumeInternal)
            {
                $m = $MountPoint[$i]
                Write-Debug "The following operation failed: Get-BitLockerVolumeInternal -MountPoint $m"
                continue
            }

            Write-Debug ("MountPoint: " + $BitLockerVolumeInternal.MountPoint)

            if ($pscmdlet.ShouldProcess($BitLockerVolumeInternal.MountPoint))
            {
                $Win32EncryptableVolume   = Get-Win32EncryptableVolumeInternal -MountPoint $BitLockerVolumeInternal.MountPoint
                $BackupKeyProtectorResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName BackupRecoveryInformationToCloudDomain -Arguments @{VolumeKeyProtectorID = $KeyProtectorId}

                if ($BackupKeyProtectorResult.ReturnValue -ne 0)
                {
                    $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $BackupKeyProtectorResult.ReturnValue
                    Write-Error -Exception $ExceptionForHr
                    continue
                }

                $ValidMountPoint = $ValidMountPoint + $MountPoint[$i]
            }
        } 

        Write-Debug "ValidMountPoint: $ValidMountPoint"

        if ($ValidMountPoint)
        {
            $BitLockerVolume = Get-BitLockerVolume -MountPoint $ValidMountPoint

            $BitLockerVolume
        }
        else
        {
            Write-Debug "No valid combination of mountpoint / protector id found"
        }

        Write-Debug "End BackupToAAD-BitLockerKeyProtector. Return $BitLockerVolume"
    }
}

#########################################################################################
# Enable-BitLockerAutoUnlock
#
# Returns BitLockerVolume structures that describes the volumes which have auto unlock enabled.
#
# Input: String[]    - array of volume names. Could be: drive letter or volume id or mounted directory
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume[]
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function Enable-BitLockerAutoUnlock
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(     
          [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string[]]
          $MountPoint)

    process
    {
       Write-Debug "Begin Enable-BitLockerAutoUnlock($MountPoint)"

       #########
       # ValidMountPoint is a subset of the elements of MountPoint array.
       # If MountPoint array contains an element that is not a valid mount point that can
       # have auto unlock enabled then the mount point is not part of ValidMountPoint
       # Only those BitLockerVolume structures are returned that are part of ValidMountPoint
       #
       # If "-whatif" is used then ValidMountPoint is always $null
       #########
       [string[]]$ValidMountPoint = $null

       for($i=0; $i -lt $MountPoint.Count; $i++)
       {
            $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint[$i]
            if (!$BitLockerVolumeInternal)
            {
                $m = $MountPoint[$i]
                Write-Debug "The following operation failed: Get-BitLockerVolumeInternal -MountPoint $m"
                continue
            }

            Write-Debug ("MountPoint: " + $BitLockerVolumeInternal.MountPoint)

            if ($pscmdlet.ShouldProcess($BitLockerVolumeInternal.MountPoint))
            {
                $Win32EncryptableVolume     = Get-Win32EncryptableVolumeInternal -MountPoint $BitLockerVolumeInternal.MountPoint
                
                $IsAutoUnlockEnabledResult  = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName IsAutoUnlockEnabled
            
                if ($IsAutoUnlockEnabledResult.ReturnValue -ne 0)
                {
                    $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $IsAutoUnlockEnabledResult.ReturnValue
                    Write-Error -Exception $ExceptionForHr
                    continue
                }

                if ($IsAutoUnlockEnabledResult.IsAutoUnlockEnabled -eq $false)
                {
                    $ProtectKeyWithExternalKeyResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName ProtectKeyWithExternalKey
                    if ($ProtectKeyWithExternalKeyResult.ReturnValue -ne 0)
                    {
                        $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $ProtectKeyWithExternalKeyResult.ReturnValue
                        Write-Error -Exception $ExceptionForHr
                        continue
                    }

                    Write-Debug ("Added Protector: " + $ProtectKeyWithExternalKeyResult.VolumeKeyProtectorID)

                    $EnableAutoUnlockResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName EnableAutoUnlock -Arguments @{VolumeKeyProtectorID = $ProtectKeyWithExternalKeyResult.VolumeKeyProtectorID}
                    if ($EnableAutoUnlockResult.ReturnValue -ne 0)
                    {
                        $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $EnableAutoUnlockResult.ReturnValue
                        Write-Error -Exception $ExceptionForHr
                        continue
                    }
                }

                $ValidMountPoint = $ValidMountPoint + $MountPoint[$i]
            }

        } 

        Write-Debug "ValidMountPoint: $ValidMountPoint"

        if ($ValidMountPoint)
        {
            $BitLockerVolume = Get-BitLockerVolume -MountPoint $ValidMountPoint

            $BitLockerVolume
        }
        else
        {
            Write-Debug "No valid mount point was provided that can have auto unlock enabled"
        }


        Write-Debug "End Enable-BitLockerAutoUnlock. Return $BitLockerVolume"
    }
}



#########################################################################################
# Disable-BitLockerAutoUnlock
#
# Returns BitLockerVolume structures that describes the volumes which have auto unlock disabled.
#
# Input: String[]    - array of volume names. Could be: drive letter or volume id or mounted directory
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume[]
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function Disable-BitLockerAutoUnlock
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
          [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string[]]
          $MountPoint)

    process
    {
       Write-Debug "Begin Disable-BitLockerAutoUnlock($MountPoint)"

       #########
       # ValidMountPoint is a subset of the elements of MountPoint array.
       # If MountPoint array contains an element that is not a valid mount point that can
       # have auto unlock enabled then the mount point is not part of ValidMountPoint
       # Only those BitLockerVolume structures are returned that are part of ValidMountPoint
       #
       # If "-whatif" is used then ValidMountPoint is always $null
       #########
       [string[]]$ValidMountPoint = $null

       for($i=0; $i -lt $MountPoint.Count; $i++)
       {
            $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint[$i]
            if (!$BitLockerVolumeInternal)
            {
                $m = $MountPoint[$i]
                Write-Debug "The following operation failed: Get-BitLockerVolumeInternal -MountPoint $m"
                continue
            }

            Write-Debug ("MountPoint: " + $BitLockerVolumeInternal.MountPoint)

            if ($pscmdlet.ShouldProcess($BitLockerVolumeInternal.MountPoint))
            {
                $Win32EncryptableVolume     = Get-Win32EncryptableVolumeInternal -MountPoint $BitLockerVolumeInternal.MountPoint

                $IsAutoUnlockEnabledResult  = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName IsAutoUnlockEnabled

                if ($IsAutoUnlockEnabledResult.ReturnValue -ne 0)
                {
                    $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $IsAutoUnlockEnabledResult.ReturnValue
                    Write-Error -Exception $ExceptionForHr
                    continue
                }

                if ($IsAutoUnlockEnabledResult.IsAutoUnlockEnabled -eq $true)
                {

                    $DisableAutoUnlockResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName DisableAutoUnlock
                    if ($DisableAutoUnlockResult.ReturnValue -ne 0)
                    {
                        $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $DisableAutoUnlockResult.ReturnValue
                        Write-Error -Exception $ExceptionForHr
                        continue
                    }
                }

                $ValidMountPoint = $ValidMountPoint + $MountPoint[$i]
            }

        }

        Write-Debug "ValidMountPoint: $ValidMountPoint"

        if ($ValidMountPoint)
        {
            $BitLockerVolume = Get-BitLockerVolume -MountPoint $ValidMountPoint

            $BitLockerVolume
        }
        else
        {
            Write-Debug "No valid mount point was provided that can have auto unlock enabled"
        }


        Write-Debug "End Disable-BitLockerAutoUnlock. Return $BitLockerVolume"
    }
}


#########################################################################################
# Disable-BitLocker
#
# Returns BitLockerVolume structures that describe the volumes which have been disabled.
#
# Input: String[]    - array of volume names. Could be: drive letter or volume id or mounted directory
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume[]
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function Disable-BitLocker
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param(
          [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string[]]
          $MountPoint)

    process
    {
       Write-Debug "Begin Disable-BitLocker($MountPoint)"

       #########
       # ValidMountPoint is a subset of the elements of MountPoint array.
       # If MountPoint array contains an element that is not a valid mount point that can
       # have auto unlock enabled then the mount point is not part of ValidMountPoint
       # Only those BitLockerVolume structures are returned that are part of ValidMountPoint
       #
       # If "-whatif" is used then ValidMountPoint is always $null
       #########
       [string[]]$ValidMountPoint = $null

       for($i=0; $i -lt $MountPoint.Count; $i++)
       {
            $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint[$i]
            if (!$BitLockerVolumeInternal)
            {
                $m = $MountPoint[$i]
                Write-Debug "The following operation failed: Get-BitLockerVolumeInternal -MountPoint $m"
                continue
            }

            Write-Debug ("MountPoint: " + $BitLockerVolumeInternal.MountPoint)

            if ($pscmdlet.ShouldProcess($BitLockerVolumeInternal.MountPoint))
            {
                $Win32EncryptableVolume      = Get-Win32EncryptableVolumeInternal -MountPoint $BitLockerVolumeInternal.MountPoint

                if ($BitLockerVolumeInternal.VolumeType -eq [Microsoft.BitLocker.Structures.BitLockerVolumeType]::OperatingSystem)
                {
                    $IsAutoUnlockKeyStoredResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName IsAutoUnlockKeyStored
                
                    if ($IsAutoUnlockKeyStoredResult.ReturnValue -ne 0)
                    {
                        $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $IsAutoUnlockKeyStoredResult.ReturnValue
                        Write-Error -Exception $ExceptionForHr
                        continue
                    }

                    if ($IsAutoUnlockKeyStoredResult.IsAutoUnlockKeyStored -eq $true)
                    {
                        $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $E_AUTOUNLOCK_ENABLED
                        Write-Error -Exception $ExceptionForHr

                        continue
                    }
                }

                $DecryptResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName Decrypt
                if ($DecryptResult.ReturnValue -ne 0)
                {
                    $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $DecryptResult.ReturnValue
                    Write-Error -Exception $ExceptionForHr
                    continue
                }

                $ValidMountPoint = $ValidMountPoint + $MountPoint[$i]
            }

        }

        Write-Debug "ValidMountPoint: $ValidMountPoint"

        if ($ValidMountPoint)
        {
            $BitLockerVolume = Get-BitLockerVolume -MountPoint $ValidMountPoint

            $BitLockerVolume
        }
        else
        {
            Write-Debug "No valid mount point was provided that can be decrypted"
        }


        Write-Debug "End Disable-BitLocker. Return $BitLockerVolume"
    }
}



#########################################################################################
# Clear-BitLockerAutoUnlock
#
# Returns BitLockerVolume structure that describes the OS volume which has had auto unlock
# keys cleared.
#
# Input: None
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function Clear-BitLockerAutoUnlock
{
    [CmdletBinding()]
    Param()
    process
    {
        Write-Debug "Begin Clear-BitLockerAutoUnlock."

        $OsBitLockerVolume = Get-BitLockerVolume | where {$_.VolumeType -eq [Microsoft.BitLocker.Structures.BitLockerVolumeType]::OperatingSystem}

        if ($OsBitLockerVolume -eq $null)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $E_NOTFOUND
            $ErrorMessage = [string]::Format($stringTable.ErrorOperatingSystemMountPointNotFound)
            Write-Error -Exception $ExceptionForHr -Message $ErrorMessage

        }
        Write-Debug "Operating system volume to operate on: $OsBitLockerVolume."

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $OsBitLockerVolume.MountPoint

        $ClearKeysResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName ClearAllAutoUnlockKeys
            
        if ($ClearKeysResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $ClearKeysResult.ReturnValue
            Write-Error -Exception $ExceptionForHr

            $OsBitLockerVolume = $null
        }
        else
        {
            #
            # Since we modified the OS Volume by clearing all auto unlock keys, we should
            # get a fresh BitLockerVolume
            #
            $OsBitLockerVolume = Get-BitLockerVolume $OsBitLockerVolume
        }

        $OsBitLockerVolume

        Write-Debug "End Clear-BitLockerAutoUnlock. Return $OsBitLockerVolume"
    }
}


#########################################################################################
# Unlock-AdAccountOrGroupInternal
#
# Returns BitLockerVolume structure that describes an unlocked volume. The volume is unlocked
# based on the current user/machine token.
#
# Input: MountPoint
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume
#########################################################################################
function Unlock-AdAccountOrGroupInternal
{
    Param(
          [Parameter(Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint)

    process
    {
        Write-Debug "Begin Unlock-AdAccountOrGroupInternal($MountPoint)"

        $BitLockerVolume        = $null
        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint

        $UnlockWithAdSidResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName UnlockWithAdSid

        if ($UnlockWithAdSidResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $UnlockWithAdSidResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $UnlockWithAdSidResult.ReturnValue
        }

        return 0
        Write-Debug "End Unlock-AdAccountOrGroupInternal. Return $BitLockerVolume"
    }
}

#########################################################################################
# Test-SystemEntropyForBitLocker
#
# Returns true or false
#
# Input: None
#
# Return: $true or $false
#########################################################################################
function Test-SystemEntropyForBitLockerInternal
{
    process
    {
        Write-Debug "Start Test-SystemEntropyForBitLockerInternal"

        $IsWinPe = $false

        $RegistyItem = Get-Item HKLM:\SYSTEM\CurrentControlSet\Control\MiniNT -ErrorAction SilentlyContinue

        if ($RegistryItem -eq $null)
        {
            return $true
        }

        $IsWinPe = $true
        Write-Debug "WinPe: $IsWinPe"

        $Win32Tpm = Get-CimInstance -ClassName Win32_Tpm -Namespace "root\CIMV2\Security\MicrosoftTpm"

        if ($Win32Tpm -eq $null)
        {
            Write-Debug "Tpm WMI object could not not be created"
            return $false
        }


        $IsEnabled              = $false
        $IsActivated            = $false

        $IsEnabledResult = Invoke-CimMethod -InputObject $Win32Tpm -MethodName IsEnabled
        if ($IsEnabledResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $IsEnabledResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $false
        }
        else
        {
            $IsEnabled = $IsEnabledResult.IsEnabled
        }

        $IsActivatedResult = Invoke-CimMethod -InputObject $Win32Tpm -MethodName IsActivated
        if ($IsActivatedResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $IsActivatedResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $false
        }
        else
        {
            $IsActivated = $IsActivatedResult.IsActivated
        }

        Write-Debug "End Test-SystemEntropyForBitLockerInternal. IsEnabled: $IsEnabled   IsActivated: $IsActivated"
        return $IsEnabled -and $IsActivated
    }
}


#########################################################################################
# Test-TpmProtectorNeededInternal
#
# Returns true or false
#
# Input: None
#
# Return: $true or $false
#########################################################################################
function Test-TpmProtectorNeededInternal
{
    Param(
          [Parameter(Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint,

          [Parameter(Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $ParameterSetNameFromEnableBitLocker)

    process
    {
        Write-Debug "Begin Test-TpmProtectorNeededInternal"

        $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint
        if ($BitLockerVolumeInternal -eq $false)
        {
            Write-Debug "End Test-TpmProtectorNeededInternal. Return $false. BitLockerVolume not valid"
            return $false
        }

        if ($BitLockerVolumeInternal.VolumeType -ne [Microsoft.BitLocker.Structures.BitLockerVolumeType]::OperatingSystem)
        {
            Write-Debug "End Test-TpmProtectorNeededInternal. Return $false. BitLockerVolume is not an OS volume"
            return $false
        }

        $DoesPasswordProtectorExist = $BitLockerVolumeInternal.KeyProtector | where-object {$_.KeyProtectorType -eq [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::Password}
        $IsTpmReady = Test-TpmForBitLockerInternal

        if ($IsTpmReady -eq $true)
        {
            $AnyTpmKeyProtectorType   = [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::Tpm,
                                        [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::TpmNetworkKey,
                                        [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::TpmPin,
                                        [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::TpmPinStartupKey,
                                        [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::TpmStartupKey

            $DoesAnyTpmProtectorExist = $BitLockerVolumeInternal.KeyProtector | where-object {$AnyTpmKeyProtectorType -contains $_.KeyProtectorType}

            #
            # Check if we don't have the following:
            #  -  Password protector
            #  -  ANY Tpm protector
            #  -  User trying to add a password protector
            #  -  User trying to add ANY Tpm protector
            #
            if ($DoesPasswordProtectorExist -eq $null                                       -and
                $DoesAnyTpmProtectorExist -eq $null                                         -and
                $ParameterSetNameFromEnableBitLocker -ne "PasswordProtector"                -and
                $ParameterSetNameFromEnableBitLocker -ne "TpmProtector"                     -and
                $ParameterSetNameFromEnableBitLocker -ne "TpmAndStartupKeyProtector"        -and
                $ParameterSetNameFromEnableBitLocker -ne "TpmAndPinProtector"               -and
                $ParameterSetNameFromEnableBitLocker -ne "TpmAndPinAndStartupKeyProtector")
            {
                 Write-Debug "End Test-TpmProtectorNeededInternal. Return $true"
                 return $true
            }

         }

        return $false
        Write-Debug "End Test-TpmProtectorNeededInternal. Return $false"
    }

}


#########################################################################################
# Test-TpmForBitLockerInternal
#
# Returns true or false
#
# Input: None
#
# Return: $true or $false
#########################################################################################
function Test-TpmForBitLockerInternal
{
    process
    {
        Write-Debug "Begin Test-TpmForBitLockerInternal"

        $IsEnabled              = $false
        $IsOwned                = $false
        $IsActivated            = $false
        $IsSrkAuthCompatible    = $false

        $Win32Tpm = Get-CimInstance -ClassName Win32_Tpm -Namespace "root\CIMV2\Security\MicrosoftTpm"

        if ($Win32Tpm -eq $null)
        {
            Write-Debug "Tpm WMI object could not not be created"
            return $false
        }

        $IsEnabledResult = Invoke-CimMethod -InputObject $Win32Tpm -MethodName IsEnabled
        if ($IsEnabledResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $IsEnabledResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $false
         }
        else
        {
            $IsEnabled = $IsEnabledResult.IsEnabled
        }

        $IsOwnedResult = Invoke-CimMethod -InputObject $Win32Tpm -MethodName IsOwned
        if ($IsOwnedResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $IsOwnedResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $false
        }
        else
        {
            $IsOwned = $IsOwnedResult.IsOwned
        }

        $IsActivatedResult = Invoke-CimMethod -InputObject $Win32Tpm -MethodName IsActivated
        if ($IsActivatedResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $IsActivatedResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $false
        }
        else
        {
            $IsActivated = $IsActivatedResult.IsActivated
        }

        $IsSrkAuthCompatibleResult = Invoke-CimMethod -InputObject $Win32Tpm -MethodName IsSrkAuthCompatible
        if ($IsSrkAuthCompatibleResult.ReturnValue -ne 0)
        {
            $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $IsSrkAuthCompatibleResult.ReturnValue
            Write-Error -Exception $ExceptionForHr
            return $false
        }
        else
        {
            $IsSrkAuthCompatible = $IsSrkAuthCompatibleResult.IsSrkAuthCompatible
        }


        Write-Debug "End Test-TpmForBitLockerInternal. IsEnabled: $IsEnabled   IsOwned: $IsOwned    IsActivated: $IsActivated    IsSrkAuthCompatible: $IsSrkAuthCompatible"

        return $IsEnabled -and $IsOwned -and $IsActivated -and $IsSrkAuthCompatible
    }
}

function Enable-BitLockerInternal
{
    Param(
          [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint,

          [Parameter(Mandatory = $false)]
          [Microsoft.BitLocker.Structures.BitLockerVolumeEncryptionMethodOnEnable]
          $EncryptionMethod,

          [Parameter(Mandatory = $true)]
          [bool]
          $SkipHardwareTest,

          [Parameter(Mandatory = $true)]
          [bool]
          $UsedSpaceOnly)

    process
    {
        Write-Debug "Begin Enable-BitLockerInternal. MountPoint: $MountPoint   EncryptionMethod: $EncryptionMethod   SkipHardwareTest: $SkipHardwareTest    UsedSpaceOnly: $UsedSpaceOnly"

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint
        if ($Win32EncryptableVolume -eq $null)
        {
            Write-Debug "Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint returned null"
            return
        }

        $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint
        if ($BitLockerVolumeInternal -eq $null)
        {
            Write-Debug "Get-BitLockerVolumeInternal -MountPoint $MountPoint returned null"
            return
        }

        $EncryptionFlags = 0
        if ($UsedSpaceOnly -eq $true)
        {
            $EncryptionFlags = $FVE_CONV_FLAG_DATAONLY
        }


        $IntegerEncryptionMethod = 0 # None which means WMI layer picks encryption method
        if ($EncryptionMethod -ne $null)
        {
            [int]$IntegerEncryptionMethod = $EncryptionMethod
        }


        if ($SkipHardwareTest -eq $true)
        {
            $EncryptResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName Encrypt -Arguments @{EncryptionMethod = [uint32]$IntegerEncryptionMethod; EncryptionFlags = [uint32]$EncryptionFlags}
            if ($EncryptResult.ReturnValue -ne 0)
            {
                $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $EncryptResult.ReturnValue
                Write-Error -Exception $ExceptionForHr
                return
            }
        }
        else
        {
            $EncryptResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName EncryptAfterHardwareTest -Arguments @{EncryptionMethod = [uint32]$IntegerEncryptionMethod; EncryptionFlags = [uint32]$EncryptionFlags}
            if ($EncryptResult.ReturnValue -ne 0)
            {
                $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $EncryptResult.ReturnValue
                Write-Error -Exception $ExceptionForHr
                return
            }
        }

        $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint
        $BitLockerVolumeInternal
        Write-Debug "End Enable-BitLockerInternal. BitLockerVolumeInternal: $BitLockerVolumeInternal"
    }
}

#########################################################################################
#
#########################################################################################
function Show-BitLockerRequiredActionsInternal
{
    Param(
          [Parameter(Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint,

          [Parameter(Mandatory = $true)]
          [bool]
          $SkipHardwareTest)

    process
    {
        Write-Debug "Begin Show-BitLockerRequiredActionsInternal. MountPoint: $MountPoint   SkipHardwareTest: $SkipHardwareTest"


        $HardwareTestStatus = $FVE_HARDWARE_TEST_NOT_FAILED_OR_PENDING
        $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint

        if ($BitLockerVolumeInternal -eq $null)
        {
            Write-Debug "Get-BitLockerVolumeInternal -MountPoint $MountPoint returned null"
            return
        }

        $AnyExternalKeyProtectorType = [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::ExternalKey,
                                       [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::TpmAndExternalKey,
                                       [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::TpmAndPinAndExternalKey

        $RecoveryKeyProtector    = $BitLockerVolumeInternal.KeyProtector | Where {$_.KeyProtectorType -eq [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::RecoveryPassword} | Select-Object -First 1
        $AnyExternalKeyProtector = $BitLockerVolumeInternal.KeyProtector | Where {$AnyExternalKeyProtectorType -contains $_.KeyProtectorType} | Select-Object -First 1


        if ($SkipHardwareTest -eq $false)
        {
            $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint
            if ($Win32EncryptableVolume -eq $null)
            {
                Write-Debug "Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint returned null"
                return
            }

            $HardwareTestStatusResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName GetHardwareTestStatus
            if ($HardwareTestStatusResult.ReturnValue -ne 0)
            {
                $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $HardwareTestStatusResult.ReturnValue
                Write-Error -Exception $ExceptionForHr
                return
            }

            $HardwareTestStatus = $HardwareTestStatusResult.TestStatus
        }

        if ($HardwareTestStatus -eq $FVE_HARDWARE_TEST_NOT_FAILED_OR_PENDING -and
            $RecoveryKeyProtector -eq $null)
        {
            Write-Debug "End Show-BitLockerRequiredActionsInternal. HardwareTestStatus is not failed or pending"
            return   
        }



        if ($RecoveryKeyProtector -ne $null    -and 
            $HardwareTestStatus -eq $FVE_HARDWARE_TEST_NOT_FAILED_OR_PENDING)
        {
            $Message = [string]::Format($stringTable.WarningWriteDownRecoveryPassword, $RecoveryKeyProtector.RecoveryPassword, [Environment]::NewLine)
            Write-Warning $Message
        }
        elseif ($RecoveryKeyProtector -ne $null    -and 
            $AnyExternalKeyProtector -ne $null -and 
            $HardwareTestStatus -eq $FVE_HARDWARE_TEST_PENDING)
        {
            $Message = [string]::Format($stringTable.WarningWriteDownRecoveryPasswordInsertExternalKeyRestart, $RecoveryKeyProtector.RecoveryPassword, [Environment]::NewLine)
            Write-Warning $Message
        }
        elseif ($RecoveryKeyProtector -eq $null     -and
                 $AnyExternalKeyProtector -ne $null -and 
                 $HardwareTestStatus -eq $FVE_HARDWARE_TEST_PENDING)
        {
            $Message = [string]::Format($stringTable.WarningInsertExternalKeyRestart, [Environment]::NewLine)
            Write-Warning $Message
        }
        elseif ($RecoveryKeyProtector -ne $null     -and
                 $AnyExternalKeyProtector -eq $null -and 
                 $HardwareTestStatus -eq $FVE_HARDWARE_TEST_PENDING)
        {
            $Message = [string]::Format($stringTable.WarningWriteDownRecoveryPasswordRestart, $RecoveryKeyProtector.RecoveryPassword, [Environment]::NewLine)
            Write-Warning $Message
        }      
        elseif ($RecoveryKeyProtector -eq $null     -and
                 $AnyExternalKeyProtector -eq $null -and 
                 $HardwareTestStatus -eq $FVE_HARDWARE_TEST_PENDING)
        {
            $Message = [string]::Format($stringTable.WarningRestart, [Environment]::NewLine)
            Write-Warning $Message
        }
        elseif ($HardwareTestStatus -eq $FVE_HARDWARE_TEST_FAILED)
        {
            $Message = [string]::Format($StringTable.WarningHardwareTestFailed, [Environment]::NewLine)
            Write-Warning $Message
        }

    }
}

#########################################################################################
#
#########################################################################################
function Get-RecoveryKeyProtectorsCountInternal
{
    Param(
          [Parameter(Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint)

    process
    {
        $RecoveryKeyProtectorTypes = [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::PublicKey,
                                     [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::Sid,
                                     [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::ExternalKey,
                                     [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::Password,
                                     [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::RecoveryPassword

        [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtector[]] $KeyProtectors = (Get-BitLockerVolumeInternal -MountPoint $MountPoint).KeyProtector | where {$RecoveryKeyProtectorTypes -contains $_.KeyProtectorType}
        if ($KeyProtectors -eq $null)
        {
            return 0
        }

        return $KeyProtectors.Count
    }
}

#########################################################################################
#
#########################################################################################

function Set-BitLockerVolumeInternal
{
    Param(
          [Parameter(Mandatory = $true)]
          [ValidateNotNullOrEmpty()]
          [string]
          $MountPoint,
      
          [Parameter(Mandatory = $true)]
          [ValidateRange(0,2)] #[($FVE_FORCE_ENCRYPTION_TYPE_UNSPECIFIED,$FVE_FORCE_ENCRYPTION_TYPE_HARDWARE)]
          [int]
          $ForceEncryptionType,

          [Parameter(Mandatory = $true)]
          [bool]
          $UsedSpaceOnly)

    process
    {
        [int]$InitializationFlags = $ForceEncryptionType

        $Win32EncryptableVolume = Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint
        if ($Win32EncryptableVolume -eq $null)
        {
            Write-Debug "Get-Win32EncryptableVolumeInternal -MountPoint $MountPoint returned null"
            return $null
        }

        if ($UsedSpaceOnly -eq $true)
        {
            $InitializationFlags += $FVE_PROVISIONING_MODIFIER_USED_SPACE;
        }

        $PrepareVolumeResult = Invoke-CimMethod -InputObject $Win32EncryptableVolume -MethodName PrepareVolumeEx -Arguments @{DiscoveryVolumeType = $DEFAULT_DISCOVERY_VOLUME_TYPE; InitializationFlags = [uint32]$InitializationFlags}
        if ($PrepareVolumeResult.ReturnValue -ne 0)
        {
            if (($PrepareVolumeResult.ReturnValue -ne $FVE_E_NOT_DECRYPTED) -or
                (($InitializationFlags -ne 0) -and
                 ($InitializationFlags -ne $FVE_PROVISIONING_MODIFIER_USED_SPACE)))
            {
                #
                # The volume may have been previously implicitly initialized
                # through adding protectors. So we should not fail unless we
                # were passing some explicit initialization flags.
                # We also allow remapping on used-space flag because we are
                # going to pass used-space flag later when starting conversion.
                #
                $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $PrepareVolumeResult.ReturnValue
                Write-Error -Exception $ExceptionForHr
                return $null
            }
        }

        Get-BitLockerVolumeInternal -MountPoint $MountPoint
    }
}

#########################################################################################
# Enable-BitLocker
#
# Returns BitLockerVolume structures that describes volumes with bitlocker enabled. 
#
# Input: String[]    - array of volume names. Could be: drive letter or volume id or mounted directory
#
# Return: Microsoft.BitLocker.Structures.BitLockerVolume[]
#########################################################################################

#.ExternalHelp Bitlocker.psm1-help.xml
function Enable-BitLocker
{
    [CmdletBinding(SupportsShouldProcess=$true)]

    Param(
          [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true)]
          [ValidateNotNullOrEmpty()]
          [string[]]
          $MountPoint,

          [Parameter(Mandatory = $false)]
          [Microsoft.BitLocker.Structures.BitLockerVolumeEncryptionMethodOnEnable]
          $EncryptionMethod,

          [Parameter(Mandatory = $false)]
          [System.Management.Automation.SwitchParameter]
          $HardwareEncryption = $false,

          [Parameter(Mandatory = $false)]
          [System.Management.Automation.SwitchParameter]
          [Alias("s")]
          $SkipHardwareTest = $false,

          [Parameter(Mandatory = $false)]
          [System.Management.Automation.SwitchParameter]
          [Alias("qe")]
          $UsedSpaceOnly = $false,

          # 
          # Password Protector 
          #

          [Parameter(Mandatory = $true, ParameterSetName="PasswordProtector")]
          [Alias("pwp")]
          [System.Management.Automation.SwitchParameter]
          $PasswordProtector = $false,

          [Parameter(Mandatory = $false, ParameterSetName="PasswordProtector", Position = 1)]
          [Alias("pw")]
          [System.Security.SecureString]
          $Password,

          # 
          # Recovery Password Protector 
          #

          [Parameter(Mandatory = $true, ParameterSetName="RecoveryPasswordProtector")]
          [Alias("rpp")]
          [System.Management.Automation.SwitchParameter]
          $RecoveryPasswordProtector = $false,

          [Parameter(Mandatory = $false, ParameterSetName="RecoveryPasswordProtector", Position = 1)]
          [ValidateNotNullOrEmpty()]
          [Alias("rp")]
          [String]
          $RecoveryPassword,

          # 
          # Startup Key Protector 
          #

          [Parameter(Mandatory = $true, ParameterSetName="StartupKeyProtector")]
          [Alias("skp")]
          [System.Management.Automation.SwitchParameter]
          $StartupKeyProtector = $false,

          [Parameter(Mandatory = $true, ParameterSetName="StartupKeyProtector", Position = 1)]
          [Parameter(Mandatory = $true, ParameterSetName="TpmAndPinAndStartupKeyProtector", Position = 1)]
          [Parameter(Mandatory = $true, ParameterSetName="TpmAndStartupKeyProtector", Position = 1)]
          [Alias("sk")]
          [String]
          $StartupKeyPath,

          # 
          # Active Directory Account Or Group Protector 
          #

          [Parameter(Mandatory = $true, ParameterSetName="AdAccountOrGroupProtector")]
          [Alias("sidp")]
          [System.Management.Automation.SwitchParameter]
          $AdAccountOrGroupProtector = $false,

          [Parameter(Mandatory = $false, ParameterSetName="AdAccountOrGroupProtector")]
          [System.Management.Automation.SwitchParameter]
          $Service = $false,

          [Parameter(Mandatory = $true, ParameterSetName="AdAccountOrGroupProtector", Position = 1)]
          [Alias("sid")]
          [String]
          $AdAccountOrGroup,

          # 
          # TPM And Pin And StartupKey Protector 
          #

          [Parameter(Mandatory = $true, ParameterSetName="TpmAndPinAndStartupKeyProtector")]
          [Alias("tpskp")]
          [System.Management.Automation.SwitchParameter]
          $TpmAndPinAndStartupKeyProtector = $false,

          # Defined in the StartupKeyProtector section above
          #          [Parameter(Mandatory = $true, ParameterSetName="TpmAndPinAndStartupKeyProtector", Position = 1)]
          #          [String]
          #          $StartupKeyPath,

          [Parameter(Mandatory = $false, ParameterSetName="TpmAndPinAndStartupKeyProtector", Position = 2)]
          [Parameter(Mandatory = $false, ParameterSetName="TpmAndPinProtector", Position = 1)]
          [Alias("p")]
          [System.Security.SecureString]
          $Pin,


          # 
          # TPM And Pin Protector 
          #

          [Parameter(Mandatory = $true, ParameterSetName="TpmAndPinProtector")]
          [Alias("tpp")]
          [System.Management.Automation.SwitchParameter]
          $TpmAndPinProtector = $false,

          # Defined in TPM And Pin And Startup Key Protector section above
          #          [Parameter(Mandatory = $false, ParameterSetName="TpmAndPinProtector", Position = 1)]
          #          [System.Security.SecureString]
          #          $Pin,


          # 
          # TPM And StartupKey Protector 
          #

          [Parameter(Mandatory = $true, ParameterSetName="TpmAndStartupKeyProtector")]
          [Alias("tskp")]
          [System.Management.Automation.SwitchParameter]
          $TpmAndStartupKeyProtector = $false,

          # Defined in the StartupKeyProtector section above
          #          [Parameter(Mandatory = $true, ParameterSetName="TpmAndStartupKeyProtector", Position = 1)]
          #          [String]
          #          $StartupKeyPath,

          # 
          # TPM Protector 
          #

          [Parameter(Mandatory = $true, ParameterSetName="TpmProtector")]
          [Alias("tpmp")]
          [System.Management.Automation.SwitchParameter]
          $TpmProtector = $false,

          # 
          # Recovery Key Protector 
          #

          [Parameter(Mandatory = $true, ParameterSetName="RecoveryKeyProtector")]
          [Alias("rkp")]
          [System.Management.Automation.SwitchParameter]
          $RecoveryKeyProtector = $false,

          [Parameter(Mandatory = $true, ParameterSetName="RecoveryKeyProtector", Position = 1)]
          [Alias("rk")]
          [String]
          $RecoveryKeyPath)

    process
    {

        Write-Debug "Begin Enable-BitLocker"

        #########
        # ValidMountPoint is a subset of the elements of MountPoint array.
        # If MountPoint array contains an element that is not a valid mount point then
        # the mount point is not part of ValidMountPoint
        # Only those BitLockerVolume structures are returned that are part of ValidMountPoint
        #
        # If "-whatif" is used then ValidMountPoint is always $null
        #########
        [string[]]$ValidMountPoint = $null


        if ($HardwareEncryption -eq $true -and $UsedSpaceOnly -eq $true)
        {
            $UsedSpaceOnly = $false
            Write-Warning $stringTable.WarningUsedSpaceOnlyAndHardwareEncryption
        }

        for($i=0; $i -lt $MountPoint.Count; $i++)
        {
            $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint[$i]
            if ($BitLockerVolumeInternal -eq $null)
            {
                Write-Debug ("The following operation failed: Get-BitLockerVolumeInternal -MountPoint " + $MountPoint[$i])
                continue
            }

            Write-Debug ("MountPoint: " + $BitLockerVolumeInternal.MountPoint)

            $IsFullyDecrypted = $BitLockerVolumeInternal.VolumeStatus -eq [Microsoft.BitLocker.Structures.BitLockerVolumeStatus]::FullyDecrypted
            $IsOsVolume       = $BitLockerVolumeInternal.VolumeType -eq [Microsoft.BitLocker.Structures.BitLockerVolumeType]::OperatingSystem

            if ($pscmdlet.ShouldProcess($BitLockerVolumeInternal.MountPoint))
            {
                if ($IsFullyDecrypted -eq $true)
                {
                    $IsSystemEntropyReady = Test-SystemEntropyForBitLockerInternal
                    if ($IsSystemEntropyReady -eq $false)
                    {
                        $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $TPM_E_DEACTIVATED
                        Write-Error -Exception $ExceptionForHr
                        continue
                    }

                    if ($HardwareEncryption -eq $true)
                    {
                        $BitLockerVolumeInternal = Set-BitLockerVolumeInternal -MountPoint $MountPoint[$i] -ForceEncryptionType $FVE_FORCE_ENCRYPTION_TYPE_HARDWARE -UsedSpaceOnly $false
                    }
                    else
                    {
                        $BitLockerVolumeInternal = Set-BitLockerVolumeInternal -MountPoint $MountPoint[$i] -ForceEncryptionType $FVE_FORCE_ENCRYPTION_TYPE_UNSPECIFIED -UsedSpaceOnly $UsedSpaceOnly
                    }

                    Write-Debug "Set-BitLockerVolumeInternal returned $BitLockerVolumeInternal"
                    if ($BitLockerVolumeInternal -eq $null)
                    {
                        continue
                    }

                    if ($IsOsVolume -eq $true)
                    {
                        $IsTpmReady = Test-TpmForBitLockerInternal

                        if ($IsTpmReady -eq $true)
                        {
                            $IsTpmProtectorNeeded = Test-TpmProtectorNeededInternal -MountPoint $MountPoint[$i] -ParameterSetNameFromEnableBitLocker $PsCmdlet.ParameterSetName

                            if ($IsTpmProtectorNeeded -eq $true)
                            {
                                $BitLockerVolumeInternal = Add-BitLockerKeyProtector -TpmProtector -MountPoint $BitLockerVolumeInternal
                                Write-Debug "Add-BitLockerKeyProtector returned $BitLockerVolumeInternal"
                                if ($BitLockerVolumeInternal -eq $null)
                                {
                                    continue
                                }
                            }
                        }
                        else
                        {
                            $DoesPasswordProtectorExist     = $BitLockerVolumeInternal.KeyProtector | where-object {$_.KeyProtectorType -eq [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::Password}
                            $DoesExternalKeyProtectorExist  = $BitLockerVolumeInternal.KeyProtector | where-object {$_.KeyProtectorType -eq [Microsoft.BitLocker.Structures.BitLockerVolumeKeyProtectorType]::ExternalKey}

                            if ($DoesPasswordProtectorExist -eq $null               -and
                                $DoesExternalKeyProtectorExist -eq $null            -and
                                $PsCmdlet.ParameterSetName -ne "PasswordProtector"  -and
                                $PsCmdlet.ParameterSetName -ne "StartupKeyProtector")
                            {
                                $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $E_FAIL
                                Write-Error -Exception $ExceptionForHr -Message $stringTable.ErrorExternalKeyOrPasswordRequired
                                continue
                            }
                        }

                        if ($PsCmdlet.ParameterSetName -eq "AdAccountOrGroupProtector")
                        {                        
                            $RecoveryKeyProtectorCount = Get-RecoveryKeyProtectorsCountInternal -MountPoint $MountPoint[$i]
                            $IsTpmAvailable = Test-TpmForBitLockerInternal
                            if ( ($RecoveryKeyProtectorCount -lt $MINIMUM_REQUIRED_RECOVERY_PROTECTORS_WITH_TPM -and $IsTpmAvailable -eq $true) -or ($RecoveryKeyProtectorCount -lt $MINIMUM_REQUIRED_RECOVERY_PROTECTORS_WITHOUT_TPM -and $IsTpmAvailable -eq $false))
                            {
                                $ExceptionForHr = Get-ExceptionForHrInternal -HrUInt32 $E_FAIL
                                Write-Error -Exception $ExceptionForHr -Message $stringTable.ErrorSidProtectorRequiresAdditionalRecoveryProtector
                                continue
                            }
                        }
                    } # end if OS Volume
                } #end if VolumeStatus -eq FullyDecrypted


                if ($PsCmdlet.ParameterSetName -eq "PasswordProtector")
                {
                    $BitLockerVolumeInternal = Add-BitLockerKeyProtector -MountPoint $MountPoint[$i] -PasswordProtector -Password $Password
                }
                elseif ($PsCmdlet.ParameterSetName -eq "RecoveryPasswordProtector")
                {
                    #
                    # We call the internal add protector method because we
                    # don't want it to output the recovery password. This cmdlet will do it.
                    #

                    $nResult = 0

                    if ([string]::IsNullOrEmpty($RecoveryPassword))
                    {
                        $nResult = Add-RecoveryPasswordProtectorInternal $MountPoint[$i] -SuppressWarningMessage
                    }
                    else
                    {
                        $nResult = Add-RecoveryPasswordProtectorInternal $MountPoint[$i] $RecoveryPassword -SuppressWarningMessage
                    }

                    if ($nResult -eq 0)
                    {
                         $BitLockerVolumeInternal = Get-BitLockerVolumeInternal -MountPoint $MountPoint[$i]
                    }
                }
                elseif ($PsCmdlet.ParameterSetName -eq "StartupKeyProtector")
                {
                    $BitLockerVolumeInternal = Add-BitLockerKeyProtector -MountPoint $MountPoint[$i] -StartupKeyProtector -StartupKeyPath $StartupKeyPath
                }
                elseif ($PsCmdlet.ParameterSetName -eq "AdAccountOrGroupProtector")
                {
                    if ($Service -eq $false)
                    {
                        $BitLockerVolumeInternal = Add-BitLockerKeyProtector -MountPoint $MountPoint[$i] -AdAccountOrGroupProtector -AdAccountOrGroup $AdAccountOrGroup
                    }
                    else
                    {
                        $BitLockerVolumeInternal = Add-BitLockerKeyProtector -MountPoint $MountPoint[$i] -AdAccountOrGroupProtector -AdAccountOrGroup $AdAccountOrGroup -Service
                    }
                }
                elseif ($PsCmdlet.ParameterSetName -eq "TpmAndPinAndStartupKeyProtector")
                {
                    $BitLockerVolumeInternal = Add-BitLockerKeyProtector -MountPoint $MountPoint[$i] -TpmAndPinAndStartupKeyProtector -StartupKeyPath $StartupKeyPath -Pin $Pin
                }
                elseif ($PsCmdlet.ParameterSetName -eq "TpmAndPinProtector")
                {
                    $BitLockerVolumeInternal = Add-BitLockerKeyProtector -MountPoint $MountPoint[$i] -TpmAndPinProtector -Pin $Pin
                }
                elseif ($PsCmdlet.ParameterSetName -eq "TpmAndStartupKeyProtector")
                {
                    $BitLockerVolumeInternal = Add-BitLockerKeyProtector -MountPoint $MountPoint[$i] -TpmAndStartupKeyProtector -StartupKeyPath $StartupKeyPath
                }
                elseif ($PsCmdlet.ParameterSetName -eq "TpmProtector")
                {
                    $BitLockerVolumeInternal = Add-BitLockerKeyProtector -MountPoint $MountPoint[$i] -TpmProtector
                }
                elseif ($PsCmdlet.ParameterSetName -eq "RecoveryKeyProtector")
                {
                    $BitLockerVolumeInternal = Add-BitLockerKeyProtector -MountPoint $MountPoint[$i] -RecoveryKeyProtector -RecoveryKeyPath $RecoveryKeyPath
                }

                if ($BitLockerVolumeInternal -eq $null)
                {
                    Write-Debug ("Add-BitLockerKeyProtector did not return a bitlocker volume. ParameterSet: " + $PSCmdlet.ParameterSetName)
                    continue
                }


                if ($BitLockerVolumeInternal.VolumeType -eq [Microsoft.BitLocker.Structures.BitLockerVolumeType]::Data -or
                    $IsFullyDecrypted -ne $true)
                {
                    $NeedHardwareTest = $false
                }
                else
                {
                    $NeedHardwareTest = !$SkipHardwareTest
                }

                if ($EncryptionMethod -ne $null)
                {
                    $BitLockerVolumeInternal = Enable-BitLockerInternal -MountPoint $BitLockerVolumeInternal -EncryptionMethod $EncryptionMethod -SkipHardwareTest (!$NeedHardwareTest) -UsedSpaceOnly $UsedSpaceOnly
                }
                else
                {
                    $BitLockerVolumeInternal = Enable-BitLockerInternal -MountPoint $BitLockerVolumeInternal -SkipHardwareTest (!$NeedHardwareTest) -UsedSpaceOnly $UsedSpaceOnly
                }
                Write-Debug "Enable-BitLockerInternal returned $BitLockerVolumeInternal. EncryptionMethod: $EncryptionMethod   NeedHardwareTest: $NeedHardwareTest    UsedSpaceOnly: $UsedSpaceOnly"


                if ($BitLockerVolumeInternal -eq $null)
                {
                    Write-Debug ("Could not enable bitlocker on " + $MountPoint[$i])
                    continue
                }


                Show-BitLockerRequiredActionsInternal -MountPoint $BitLockerVolumeInternal -SkipHardwareTest (!$NeedHardwareTest)

                $ValidMountPoint = $ValidMountPoint + $MountPoint[$i]

            } #end ShouldProcess
        } #end for each MountPoint


        Write-Debug "ValidMountPoint: $ValidMountPoint"

        if ($ValidMountPoint)
        {
            $BitLockerVolume = Get-BitLockerVolume -MountPoint $ValidMountPoint

            $BitLockerVolume
        }
        else
        {
            Write-Debug "No valid mount point was provided that can have bitlocker enabled"
        }


        Write-Debug "End Enable-BitLocker $BitLockerVolume"

    } #end process record
}
