modules/deploy/dsc/ext/PsGallery/xBitlocker.1.1.0.0/Misc/xBitlockerCommon.psm1

#A common function used to enable Bitlocker on a disk.
function EnableBitlocker
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $MountPoint,

        [ValidateSet("PasswordProtector","RecoveryPasswordProtector","StartupKeyProtector","TpmProtector")]
        [parameter(Mandatory = $true)]
        [System.String]
        $PrimaryProtector,

        [System.String]
        $AdAccountOrGroup,

        [System.Boolean]
        $AdAccountOrGroupProtector,

        [System.Boolean]
        $AllowImmediateReboot = $false,

        [System.Boolean]
        $AutoUnlock = $false,

        [ValidateSet("Aes128","Aes256")]
        [System.String]
        $EncryptionMethod,

        [System.Boolean]
        $HardwareEncryption,

        [System.Management.Automation.PSCredential]
        $Password,

        [System.Boolean]
        $PasswordProtector,

        [System.Management.Automation.PSCredential]
        $Pin,

        [System.String]
        $RecoveryKeyPath,

        [System.Boolean]
        $RecoveryKeyProtector,

        [System.Boolean]
        $RecoveryPasswordProtector,

        [System.Boolean]
        $Service,

        [System.Boolean]
        $SkipHardwareTest,

        [System.String]
        $StartupKeyPath,

        [System.Boolean]
        $StartupKeyProtector,

        [System.Boolean]
        $TpmProtector,

        [System.Boolean]
        $UsedSpaceOnly,

        $VerbosePreference
    )

    Write-Verbose "Beginning processing of MountPoint: $($MountPoint)"

    $blv = Get-BitLockerVolume -MountPoint $MountPoint -ErrorAction SilentlyContinue

    if ($blv -ne $null)
    {
        if ($PSBoundParameters.ContainsKey("TpmProtector") -and $PrimaryProtector -ne "TpmProtector")
        {
            throw "If TpmProtector is used, it must be the PrimaryProtector."
        }

        if ($PSBoundParameters.ContainsKey("Pin") -and !($PSBoundParameters.ContainsKey("TpmProtector")))
        {
            throw "A TpmProtector must be used if Pin is used."
        }
    
        if ($PSBoundParameters.ContainsKey("AdAccountOrGroupProtector") -and $PrimaryProtector -notlike "AdAccountOrGroupProtector" -and !(ContainsKeyProtector -Type "AdAccountOrGroup" -KeyProtectorCollection $blv.KeyProtector))
        {
            Write-Verbose "Adding AdAccountOrGroupProtector"
            Add-BitLockerKeyProtector -MountPoint $MountPoint -AdAccountOrGroupProtector -AdAccountOrGroup $AdAccountOrGroup
        }

        if ($PSBoundParameters.ContainsKey("PasswordProtector") -and $PrimaryProtector -notlike "PasswordProtector" -and !(ContainsKeyProtector -Type "Password" -KeyProtectorCollection $blv.KeyProtector))
        {
            Write-Verbose "Adding PasswordProtector"
            Add-BitLockerKeyProtector -MountPoint $MountPoint -PasswordProtector -Password $Password.Password
        }

        if ($PSBoundParameters.ContainsKey("RecoveryKeyProtector") -and $PrimaryProtector -notlike "RecoveryKeyProtector" -and !(ContainsKeyProtector -Type "ExternalKey" -KeyProtectorCollection $blv.KeyProtector))
        {
            Write-Verbose "Adding RecoveryKeyProtector"
            Add-BitLockerKeyProtector -MountPoint $MountPoint -RecoveryKeyProtector -RecoveryKeyPath $RecoveryKeyPath
        }

        if ($PSBoundParameters.ContainsKey("RecoveryPasswordProtector") -and $PrimaryProtector -notlike "RecoveryPasswordProtector" -and !(ContainsKeyProtector -Type "RecoveryPassword" -KeyProtectorCollection $blv.KeyProtector))
        {
            Write-Verbose "Adding RecoveryPasswordProtector"
            Add-BitLockerKeyProtector -MountPoint $MountPoint -RecoveryPasswordProtector
        }

        if ($PSBoundParameters.ContainsKey("StartupKeyProtector") -and $PrimaryProtector -notlike "TpmProtector" -and $PrimaryProtector -notlike "StartupKeyProtector" -and !(ContainsKeyProtector -Type "ExternalKey" -KeyProtectorCollection $blv.KeyProtector))
        {
            Write-Verbose "Adding StartupKeyProtector"
            Add-BitLockerKeyProtector -MountPoint $MountPoint -StartupKeyProtector -StartupKeyPath $StartupKeyPath
        }

        if ($PSBoundParameters.ContainsKey("TpmProtector") -and $PrimaryProtector -notlike "TpmProtector" -and !(ContainsKeyProtector -Type "Tpm" -KeyProtectorCollection $blv.KeyProtector -StartsWith $true))
        {
            Write-Verbose "Adding TpmProtector"
            Add-BitLockerKeyProtector -MountPoint $MountPoint -TpmProtector $TpmProtector
        }

        #Now enable Bitlocker with the primary key protector
        if ($blv.VolumeStatus -eq "FullyDecrypted")
        {
            #First add non-key related parameters
            $params = @{}
            $params.Add("MountPoint", $MountPoint)

            if ($PSBoundParameters.ContainsKey("EncryptionMethod"))
            {
                $params.Add("EncryptionMethod", $EncryptionMethod)
            }

            if ($PSBoundParameters.ContainsKey("HardwareEncryption"))
            {
                $params.Add("HardwareEncryption", $true)
            }

            if ($PSBoundParameters.ContainsKey("Service"))
            {
                $params.Add("Service", $true)
            }

            if ($PSBoundParameters.ContainsKey("SkipHardwareTest"))
            {
                $params.Add("SkipHardwareTest", $true)
            }

            if ($PSBoundParameters.ContainsKey("UsedSpaceOnly"))
            {
                $params.Add("UsedSpaceOnly", $true)
            }

            #Now add the primary protector
            $handledTpmAlready = $false

            #Deal with a couple one off cases
            if ($PSBoundParameters.ContainsKey("Pin"))
            {
                $handledTpmAlready = $true

                $params.Add("Pin", $Pin.Password)
                
                if ($PSBoundParameters.ContainsKey("StartupKeyProtector"))
                {
                    $params.Add("TpmAndPinAndStartupKeyProtector", $true)
                    $params.Add("StartupKeyPath", $StartupKeyPath)
                }
                else
                {
                    $params.Add("TpmAndPinProtector", $true)
                }
            }

            if ($PSBoundParameters.ContainsKey("StartupKeyProtector") -and $PrimaryProtector -like "TpmProtector" -and $handledTpmAlready -eq $false)
            {
                $handledTpmAlready = $true

                $params.Add("TpmAndStartupKeyProtector", $true)
                $params.Add("StartupKeyPath", $StartupKeyPath)                
            }


            #Now deal with the standard primary protectors
            if ($PrimaryProtector -like "PasswordProtector")
            {
                $params.Add("PasswordProtector", $true)
                $params.Add("Password", $Password.Password)
            }
            elseif ($PrimaryProtector -like "RecoveryPasswordProtector")
            {
                $params.Add("RecoveryPasswordProtector", $true)
            }
            elseif ($PrimaryProtector -like "StartupKeyProtector")
            {
                $params.Add("StartupKeyProtector", $true)
                $params.Add("StartupKeyPath", $StartupKeyPath)
            }
            elseif ($PrimaryProtector -like "TpmProtector" -and $handledTpmAlready -eq $false)
            {
                $params.Add("TpmProtector", $true)
            }

            #Run Enable-Bitlocker
            Write-Verbose "Running Enable-Bitlocker"

            $newBlv = Enable-Bitlocker @params

            #Check if the Enable succeeded
            if ($newBlv -ne $null)
            {
                if ($blv.VolumeType -eq "OperatingSystem") #Only initiate reboot if this is an OS drive
                {
                    $global:DSCMachineStatus = 1

                    if ($AllowImmediateReboot -eq $true)
                    {
                        Write-Verbose "Forcing an immediate reboot of the computer in 30 seconds"

                        Start-Sleep -Seconds 30
                        Restart-Computer -Force
                    }
                }
            }
            else
            {
                throw "Failed to successfully enable Bitlocker on MountPoint $($MountPoint)"
            }

            #Finally, enable AutoUnlock if requested
            if ($AutoUnlock -eq $true -and $blv.VolumeType -ne "OperatingSystem")
            {
                Enable-BitlockerAutoUnlock -MountPoint $MountPoint
            }
        }
    }
    else
    {
        throw "Unable to find Bitlocker Volume associated with Mount Point '$($MountPoint)'"
    }
}

#A common function used to test if Bitlocker is enabled on a disk with the appropriate settings
function TestBitlocker
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $MountPoint,

        [ValidateSet("PasswordProtector","RecoveryPasswordProtector","StartupKeyProtector","TpmProtector")]
        [parameter(Mandatory = $true)]
        [System.String]
        $PrimaryProtector,

        [System.String]
        $AdAccountOrGroup,

        [System.Boolean]
        $AdAccountOrGroupProtector,

        [System.Boolean]
        $AllowImmediateReboot = $false,

        [System.Boolean]
        $AutoUnlock = $false,

        [ValidateSet("Aes128","Aes256")]
        [System.String]
        $EncryptionMethod,

        [System.Boolean]
        $HardwareEncryption,

        [System.Management.Automation.PSCredential]
        $Password,

        [System.Boolean]
        $PasswordProtector,

        [System.Management.Automation.PSCredential]
        $Pin,

        [System.String]
        $RecoveryKeyPath,

        [System.Boolean]
        $RecoveryKeyProtector,

        [System.Boolean]
        $RecoveryPasswordProtector,

        [System.Boolean]
        $Service,

        [System.Boolean]
        $SkipHardwareTest,

        [System.String]
        $StartupKeyPath,

        [System.Boolean]
        $StartupKeyProtector,

        [System.Boolean]
        $TpmProtector,

        [System.Boolean]
        $UsedSpaceOnly,

        $VerbosePreference
    )

    $blv = Get-BitLockerVolume -MountPoint $MountPoint -ErrorAction SilentlyContinue

    if ($blv -eq $null)
    {
        Write-Verbose "Unable to locate MountPoint: $($MountPoint)"
        return $false
    }
    elseif ($blv.KeyProtector -eq $null -or $blv.KeyProtector.Count -eq 0)
    {
        Write-Verbose "No key protectors on MountPoint: $($MountPoint)"
        return $false
    }
    elseif ($AutoUnlock -eq $true -and $blv.AutoUnlockEnabled -ne $true)
    {
        Write-Verbose "AutoUnlock is not enabled for MountPoint: $($MountPoint)"
        return $false
    }
    else
    {
        if ($PSBoundParameters.ContainsKey("AdAccountOrGroupProtector") -and !(ContainsKeyProtector -Type "AdAccountOrGroup" -KeyProtectorCollection $blv.KeyProtector))
        {
            Write-Verbose "MountPoint '$($MountPoint) 'does not have AdAccountOrGroupProtector (AdAccountOrGroup)"
            return $false
        }

        if ($PSBoundParameters.ContainsKey("PasswordProtector") -and !(ContainsKeyProtector -Type "Password" -KeyProtectorCollection $blv.KeyProtector))
        {
            Write-Verbose "MountPoint '$($MountPoint) 'does not have PasswordProtector (Password)"
            return $false
        }

        if ($PSBoundParameters.ContainsKey("Pin") -and !(ContainsKeyProtector -Type "TpmPin" -KeyProtectorCollection $blv.KeyProtector -StartsWith $true))
        {
            Write-Verbose "MountPoint '$($MountPoint) 'does not have TpmPin assigned."
            return $false            
        }

        if ($PSBoundParameters.ContainsKey("RecoveryKeyProtector") -and !(ContainsKeyProtector -Type "ExternalKey" -KeyProtectorCollection $blv.KeyProtector))
        {
            Write-Verbose "MountPoint '$($MountPoint) 'does not have RecoveryKeyProtector (ExternalKey)"
            return $false
        }

        if ($PSBoundParameters.ContainsKey("RecoveryPasswordProtector") -and !(ContainsKeyProtector -Type "RecoveryPassword" -KeyProtectorCollection $blv.KeyProtector))
        {
            Write-Verbose "MountPoint '$($MountPoint) 'does not have RecoveryPasswordProtector (RecoveryPassword)"
            return $false
        }

        if ($PSBoundParameters.ContainsKey("StartupKeyProtector"))
        {
            if ($PrimaryProtector -notlike "TpmProtector")
            {
                if (!(ContainsKeyProtector -Type "ExternalKey" -KeyProtectorCollection $blv.KeyProtector))
                {
                    Write-Verbose "MountPoint '$($MountPoint) 'does not have StartupKeyProtector (ExternalKey)"
                    return $false
                }
            }
            else #TpmProtector is primary
            {
                if(!(ContainsKeyProtector -Type "Tpm" -KeyProtectorCollection $blv.KeyProtector -StartsWith $true) -and !(ContainsKeyProtector -Type "StartupKey" -KeyProtectorCollection $blv.KeyProtector -Contains $true))
                {
                    Write-Verbose "MountPoint '$($MountPoint) 'does not have TPM + StartupKey protector."
                    return $false
                }          
            }
        }

        if ($PSBoundParameters.ContainsKey("TpmProtector") -and !(ContainsKeyProtector -Type "Tpm" -KeyProtectorCollection $blv.KeyProtector -StartsWith $true))
        {
            Write-Verbose "MountPoint '$($MountPoint) 'does not have TpmProtector"
            return $false
        }
    }

    return $true
}

#Ensures that required Bitlocker prereqs are installed
function CheckForPreReqs
{
    $hasAllPreReqs = $true

    $blFeature = Get-WindowsFeature BitLocker
    $blAdminToolsFeature = Get-WindowsFeature RSAT-Feature-Tools-BitLocker
    $blAdminToolsRemoteFeature = Get-WindowsFeature RSAT-Feature-Tools-BitLocker-RemoteAdminTool

    if ($blFeature.InstallState -ne "Installed")
    {
        $hasAllPreReqs = $false

        Write-Error "The Bitlocker feature needs to be installed before the xBitlocker module can be used"
    }

    if ($blAdminToolsFeature.InstallState -ne "Installed")
    {
        $hasAllPreReqs = $false

        Write-Error "The RSAT-Feature-Tools-BitLocker feature needs to be installed before the xBitlocker module can be used"
    }

    if ($blAdminToolsRemoteFeature.InstallState -ne "Installed")
    {
        $hasAllPreReqs = $false

        Write-Error "The RSAT-Feature-Tools-BitLocker-RemoteAdminTool feature needs to be installed before the xBitlocker module can be used"
    }

    if ($hasAllPreReqs -eq $false)
    {
        throw "Required Bitlocker features need to be installed before xBitlocker can be used"
    }
}

#Checks whether the KeyProtectorCollection returned from Get-BitlockerVolume contains the specified key protector type
function ContainsKeyProtector
{
    param([string]$Type, $KeyProtectorCollection, [bool]$StartsWith = $false, [bool]$EndsWith = $false, [bool]$Contains = $false)

    if ($KeyProtectorCollection -ne $null)
    {
        foreach ($keyProtector in $KeyProtectorCollection)
        {
            if ($keyProtector.KeyProtectorType -eq $Type)
            {
                return $true
            }
            elseif ($StartsWith -eq $true -and $keyProtector.KeyProtectorType.ToString().StartsWith($Type))
            {
                return $true
            }
            elseif ($EndsWith -eq $true -and $keyProtector.KeyProtectorType.ToString().EndsWith($Type))
            {
                return $true
            }
            elseif ($Contains -eq $true -and $keyProtector.KeyProtectorType.ToString().Contains($Type))
            {
                return $true
            }
        }
    }

    return $false
}

#Takes $PSBoundParameters from another function and adds in the keys and values from the given Hashtable
function AddParameters
{
    param($PSBoundParametersIn, [Hashtable]$ParamsToAdd)

    foreach ($key in $ParamsToAdd.Keys)
    {
        if (!($PSBoundParametersIn.ContainsKey($key))) #Key doesn't exist, so add it with value
        {
            $PSBoundParametersIn.Add($key, $ParamsToAdd[$key]) | Out-Null
        }
        else #Key already exists, so just replace the value
        {
            $PSBoundParametersIn[$key] = $ParamsToAdd[$key]
        }
    }
}

#Takes $PSBoundParameters from another function. If ParamsToRemove is specified, it will remove each param.
#If ParamsToKeep is specified, everything but those params will be removed. If both ParamsToRemove and ParamsToKeep
#are specified, only ParamsToKeep will be used.
function RemoveParameters
{
    param($PSBoundParametersIn, [string[]]$ParamsToKeep, [string[]]$ParamsToRemove)

    if ($ParamsToKeep -ne $null -and $ParamsToKeep.Count -gt 0)
    {
        [string[]]$ParamsToRemove = @()

        $lowerParamsToKeep = StringArrayToLower -Array $ParamsToKeep

        foreach ($key in $PSBoundParametersIn.Keys)
        {
            if (!($lowerParamsToKeep.Contains($key.ToLower())))
            {
                $ParamsToRemove += $key
            }
        }
    }

    if ($ParamsToRemove -ne $null -and $ParamsToRemove.Count -gt 0)
    {
        foreach ($param in $ParamsToRemove)
        {
            $PSBoundParametersIn.Remove($param) | Out-Null
        }
    }
}

Export-ModuleMember -Function *