From a2d49e0c87d73ac84b35bf5742dff73516428ce0 Mon Sep 17 00:00:00 2001 From: Mike Hendrickson Date: Thu, 8 Nov 2018 10:59:32 -0800 Subject: [PATCH 1/7] Pre-rebase commit --- .../MSFT_xBLAutoBitlocker/MSFT_xBLAutoBitlocker.psm1 | 2 -- DSCResources/MSFT_xBLBitlocker/MSFT_xBLBitlocker.psm1 | 2 -- DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 | 5 ++--- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/DSCResources/MSFT_xBLAutoBitlocker/MSFT_xBLAutoBitlocker.psm1 b/DSCResources/MSFT_xBLAutoBitlocker/MSFT_xBLAutoBitlocker.psm1 index 373968a..7eeb2a4 100644 --- a/DSCResources/MSFT_xBLAutoBitlocker/MSFT_xBLAutoBitlocker.psm1 +++ b/DSCResources/MSFT_xBLAutoBitlocker/MSFT_xBLAutoBitlocker.psm1 @@ -497,5 +497,3 @@ function GetAutoBitlockerStatus } Export-ModuleMember -Function *-TargetResource - - diff --git a/DSCResources/MSFT_xBLBitlocker/MSFT_xBLBitlocker.psm1 b/DSCResources/MSFT_xBLBitlocker/MSFT_xBLBitlocker.psm1 index 36a86cf..1cc4fe5 100644 --- a/DSCResources/MSFT_xBLBitlocker/MSFT_xBLBitlocker.psm1 +++ b/DSCResources/MSFT_xBLBitlocker/MSFT_xBLBitlocker.psm1 @@ -296,5 +296,3 @@ function Test-TargetResource } Export-ModuleMember -Function *-TargetResource - - diff --git a/DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 b/DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 index 02007e2..6078906 100644 --- a/DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 +++ b/DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 @@ -5,7 +5,7 @@ function Get-TargetResource [OutputType([System.Collections.Hashtable])] param ( - [Parameter(Mandatory = $true)] + [parameter(Mandatory = $true)] [System.String] $Identity ) @@ -118,6 +118,7 @@ function Test-TargetResource $tpm = Get-Tpm + if ($null -eq $tpm) { return $false @@ -130,5 +131,3 @@ function Test-TargetResource Export-ModuleMember -Function *-TargetResource - - From 5042fbcbd2b2b5688b4c034f1961766b0d019239 Mon Sep 17 00:00:00 2001 From: Mike Hendrickson Date: Fri, 2 Nov 2018 10:24:41 -0700 Subject: [PATCH 2/7] Add remaining Unit tests for xBitlockerCommon.psm1 --- Misc/xBitlockerCommon.psm1 | 497 +++++++++++++++++---- README.md | 5 +- Tests/Unit/xBitlockerCommon.tests.ps1 | 596 ++++++++++++++++++++++++++ 3 files changed, 1022 insertions(+), 76 deletions(-) diff --git a/Misc/xBitlockerCommon.psm1 b/Misc/xBitlockerCommon.psm1 index f1d8d9d..12d87c6 100644 --- a/Misc/xBitlockerCommon.psm1 +++ b/Misc/xBitlockerCommon.psm1 @@ -1,4 +1,83 @@ -#A common function used to enable Bitlocker on a disk. +<# + .SYNOPSIS + Enables Bitlocker and Bitlocker features on the requested disk. + + .PARAMETER MountPoint + The MountPoint name as reported in Get-BitLockerVolume. + + .PARAMETER PrimaryProtector + The type of key protector that will be used as the primary key + protector. + + .PARAMETER AdAccountOrGroup + Specifies an account using the format Domain\User. + + .PARAMETER AdAccountOrGroupProtector + Indicates that BitLocker uses an AD DS account as a protector for the + volume encryption key. + + .PARAMETER AllowImmediateReboot + Whether the computer can be immediately rebooted after enabling + Bitlocker on an OS drive. Defaults to false. + + .PARAMETER AutoUnlock + Whether volumes should be enabled for auto unlock using + Enable-BitlockerAutoUnlock. + + .PARAMETER EncryptionMethod + Indicates that BitLocker uses the TPM as a protector for the volume + encryption key. + + .PARAMETER HardwareEncryption + Indicates that the volume uses hardware encryption. + + .PARAMETER Password + Specifies a secure string object that contains a password. + + .PARAMETER PasswordProtector + Indicates that BitLocker uses a password as a protector for the volume + encryption key. + + .PARAMETER Pin + Specifies a secure string object that contains a PIN. + + .PARAMETER RecoveryKeyPath + Specifies a path to a recovery key. + + .PARAMETER RecoveryKeyProtector + Indicates that BitLocker uses a recovery key as a protector for the + volume encryption key. + + .PARAMETER RecoveryPasswordProtector + Indicates that BitLocker uses a recovery password as a protector for + the volume encryption key. + + .PARAMETER Service + Indicates that the system account for this computer unlocks the + encrypted volume. + + .PARAMETER SkipHardwareTest + Indicates that BitLocker does not perform a hardware test before it + begins encryption. + + .PARAMETER StartupKeyPath + Specifies a path to a startup key. + + .PARAMETER StartupKeyProtector + Indicates that BitLocker uses a startup key as a protector for the + volume encryption key. + + .PARAMETER TpmProtector + Indicates that BitLocker uses the TPM as a protector for the volume + encryption key. + + .PARAMETER UsedSpaceOnly + Indicates that BitLocker does not encrypt disk space which contains + unused data. + + .PARAMETER VerbosePreference + Used to modify the default VerbosePreference for the function. +#> function EnableBitlocker { # Suppressing this rule because $global:DSCMachineStatus is used to trigger a reboot. @@ -113,41 +192,7 @@ function EnableBitlocker 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 - } + Add-MissingBitLockerKeyProtector @PSBoundParameters -Verbose:$VerbosePreference #Now enable Bitlocker with the primary key protector if ($blv.VolumeStatus -eq "FullyDecrypted") @@ -234,10 +279,10 @@ function EnableBitlocker #Run Enable-Bitlocker Write-Verbose "Running Enable-Bitlocker" - $newBlv = Enable-Bitlocker @params + $blv = Enable-Bitlocker @params #Check if the Enable succeeded - if ($null -ne $newBlv) + if ($null -ne $blv) { if ($blv.VolumeType -eq "OperatingSystem") #Only initiate reboot if this is an OS drive { @@ -256,12 +301,12 @@ function EnableBitlocker { 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 - } + # Finally, enable AutoUnlock if requested + if ($AutoUnlock -eq $true -and $blv.VolumeType -ne 'OperatingSystem' -and !$blv.AutoUnlockEnabled) + { + Enable-BitlockerAutoUnlock -MountPoint $MountPoint } } else @@ -270,7 +315,290 @@ function EnableBitlocker } } -#A common function used to test if Bitlocker is enabled on a disk with the appropriate settings +<# + .SYNOPSIS + Checks if any required secondary Key Protectors are missing, and adds + them to the requested volume. + + .PARAMETER MountPoint + The MountPoint name as reported in Get-BitLockerVolume. + + .PARAMETER PrimaryProtector + The type of key protector that will be used as the primary key + protector. + + .PARAMETER AdAccountOrGroup + Specifies an account using the format Domain\User. + + .PARAMETER AdAccountOrGroupProtector + Indicates that BitLocker uses an AD DS account as a protector for the + volume encryption key. + + .PARAMETER AllowImmediateReboot + Whether the computer can be immediately rebooted after enabling + Bitlocker on an OS drive. Defaults to false. + + .PARAMETER AutoUnlock + Whether volumes should be enabled for auto unlock using + Enable-BitlockerAutoUnlock. + + .PARAMETER EncryptionMethod + Indicates that BitLocker uses the TPM as a protector for the volume + encryption key. + + .PARAMETER HardwareEncryption + Indicates that the volume uses hardware encryption. + + .PARAMETER Password + Specifies a secure string object that contains a password. + + .PARAMETER PasswordProtector + Indicates that BitLocker uses a password as a protector for the volume + encryption key. + + .PARAMETER Pin + Specifies a secure string object that contains a PIN. + + .PARAMETER RecoveryKeyPath + Specifies a path to a recovery key. + + .PARAMETER RecoveryKeyProtector + Indicates that BitLocker uses a recovery key as a protector for the + volume encryption key. + + .PARAMETER RecoveryPasswordProtector + Indicates that BitLocker uses a recovery password as a protector for + the volume encryption key. + + .PARAMETER Service + Indicates that the system account for this computer unlocks the + encrypted volume. + + .PARAMETER SkipHardwareTest + Indicates that BitLocker does not perform a hardware test before it + begins encryption. + + .PARAMETER StartupKeyPath + Specifies a path to a startup key. + + .PARAMETER StartupKeyProtector + Indicates that BitLocker uses a startup key as a protector for the + volume encryption key. + + .PARAMETER TpmProtector + Indicates that BitLocker uses the TPM as a protector for the volume + encryption key. + + .PARAMETER UsedSpaceOnly + Indicates that BitLocker does not encrypt disk space which contains + unused data. + + .PARAMETER VerbosePreference + Used to modify the default VerbosePreference for the function. +#> +function Add-MissingBitLockerKeyProtector +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $MountPoint, + + [Parameter(Mandatory = $true)] + [ValidateSet("PasswordProtector","RecoveryPasswordProtector","StartupKeyProtector","TpmProtector")] + [System.String] + $PrimaryProtector, + + [Parameter()] + [System.String] + $AdAccountOrGroup, + + [Parameter()] + [System.Boolean] + $AdAccountOrGroupProtector, + + [Parameter()] + [System.Boolean] + $AllowImmediateReboot = $false, + + [Parameter()] + [System.Boolean] + $AutoUnlock = $false, + + [Parameter()] + [ValidateSet("Aes128","Aes256")] + [System.String] + $EncryptionMethod, + + [Parameter()] + [System.Boolean] + $HardwareEncryption, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Password, + + [Parameter()] + [System.Boolean] + $PasswordProtector, + + [Parameter()] + [System.Management.Automation.PSCredential] + $Pin, + + [Parameter()] + [System.String] + $RecoveryKeyPath, + + [Parameter()] + [System.Boolean] + $RecoveryKeyProtector, + + [Parameter()] + [System.Boolean] + $RecoveryPasswordProtector, + + [Parameter()] + [System.Boolean] + $Service, + + [Parameter()] + [System.Boolean] + $SkipHardwareTest, + + [Parameter()] + [System.String] + $StartupKeyPath, + + [Parameter()] + [System.Boolean] + $StartupKeyProtector, + + [Parameter()] + [System.Boolean] + $TpmProtector, + + [Parameter()] + [System.Boolean] + $UsedSpaceOnly, + + [Parameter()] + $VerbosePreference + ) + + 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 + } +} + +<# + .SYNOPSIS + Tests whether Bitlocker and the requested features have been enabled + on the target disk. + + .PARAMETER MountPoint + The MountPoint name as reported in Get-BitLockerVolume. + + .PARAMETER PrimaryProtector + The type of key protector that will be used as the primary key + protector. + + .PARAMETER AdAccountOrGroup + Specifies an account using the format Domain\User. + + .PARAMETER AdAccountOrGroupProtector + Indicates that BitLocker uses an AD DS account as a protector for the + volume encryption key. + + .PARAMETER AllowImmediateReboot + Whether the computer can be immediately rebooted after enabling + Bitlocker on an OS drive. Defaults to false. + + .PARAMETER AutoUnlock + Whether volumes should be enabled for auto unlock using + Enable-BitlockerAutoUnlock. + + .PARAMETER EncryptionMethod + Indicates that BitLocker uses the TPM as a protector for the volume + encryption key. + + .PARAMETER HardwareEncryption + Indicates that the volume uses hardware encryption. + + .PARAMETER Password + Specifies a secure string object that contains a password. + + .PARAMETER PasswordProtector + Indicates that BitLocker uses a password as a protector for the volume + encryption key. + + .PARAMETER Pin + Specifies a secure string object that contains a PIN. + + .PARAMETER RecoveryKeyPath + Specifies a path to a recovery key. + + .PARAMETER RecoveryKeyProtector + Indicates that BitLocker uses a recovery key as a protector for the + volume encryption key. + + .PARAMETER RecoveryPasswordProtector + Indicates that BitLocker uses a recovery password as a protector for + the volume encryption key. + + .PARAMETER Service + Indicates that the system account for this computer unlocks the + encrypted volume. + + .PARAMETER SkipHardwareTest + Indicates that BitLocker does not perform a hardware test before it + begins encryption. + + .PARAMETER StartupKeyPath + Specifies a path to a startup key. + + .PARAMETER StartupKeyProtector + Indicates that BitLocker uses a startup key as a protector for the + volume encryption key. + + .PARAMETER TpmProtector + Indicates that BitLocker uses the TPM as a protector for the volume + encryption key. + + .PARAMETER UsedSpaceOnly + Indicates that BitLocker does not encrypt disk space which contains + unused data. + + .PARAMETER VerbosePreference + Used to modify the default VerbosePreference for the function. +#> function TestBitlocker { [CmdletBinding()] @@ -380,7 +708,7 @@ function TestBitlocker Write-Verbose "No key protectors on MountPoint: $($MountPoint)" return $false } - elseif ($AutoUnlock -eq $true -and $blv.AutoUnlockEnabled -ne $true) + elseif ($AutoUnlock -eq $true -and $blv.AutoUnlockEnabled -ne $true -and $blv.VolumeType -ne 'OperatingSystem') { Write-Verbose "AutoUnlock is not enabled for MountPoint: $($MountPoint)" return $false @@ -499,10 +827,6 @@ function ContainsKeyProtector [bool] $StartsWith = $false, - [Parameter()] - [bool] - $EndsWith = $false, - [Parameter()] [bool] $Contains = $false @@ -520,10 +844,6 @@ function ContainsKeyProtector { 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 @@ -534,16 +854,29 @@ function ContainsKeyProtector return $false } -#Takes $PSBoundParameters from another function and adds in the keys and values from the given Hashtable +<# + .SYNOPSIS + Takes $PSBoundParameters from another function and adds in the keys and + values from the given Hashtable. + + .PARAMETER PSBoundParametersIn + The $PSBoundParameters Hashtable from the calling function. + + .PARAMETER ParamsToAdd + A Hashtable containing new Key/Value pairs to add to the given + PSBoundParametersIn Hashtable. +#> function AddParameters { + [CmdletBinding()] param ( - [Parameter()] + [Parameter(Mandatory = $true)] + [System.Object] $PSBoundParametersIn, - + [Parameter()] - [Hashtable] + [System.Collections.Hashtable] $ParamsToAdd ) @@ -560,45 +893,60 @@ function AddParameters } } -#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. +<# + .SYNOPSIS + Takes $PSBoundParameters from another function, and modifies it based + on the contents of the ParamsToRemove or ParamsToKeep parameters. If + ParamsToRemove is specified, it will remove each param. If ParamsToKeep + is specified, everything but those params will be removed. + + .PARAMETER PSBoundParametersIn + The $PSBoundParameters Hashtable from the calling function. + + .PARAMETER ParamsToKeep + A String array containing the list of parameter names to keep in the + given PSBoundParametersIn HashTable. + + .PARAMETER ParamsToRemove + A String array containing the list of parameter names to remove in the + given PSBoundParametersIn HashTable. +#> function RemoveParameters { + [CmdletBinding()] param ( - [Parameter()] + [Parameter(Mandatory = $true)] + [System.Object] $PSBoundParametersIn, - - [Parameter()] - [string[]] + + [Parameter(Mandatory = $true, ParameterSetName = 'KeepParameters')] + [System.String[]] $ParamsToKeep, - - [Parameter()] - [string[]] + + [Parameter(Mandatory = $true, ParameterSetName = 'RemoveParameters')] + [System.String[]] $ParamsToRemove ) - if ($null -ne $ParamsToKeep -and $ParamsToKeep.Count -gt 0) + if ($ParamsToKeep.Count -gt 0) { - [string[]]$ParamsToRemove = @() - - $lowerParamsToKeep = StringArrayToLower -Array $ParamsToKeep + $ParamsToKeep = $ParamsToKeep.ToLower() foreach ($key in $PSBoundParametersIn.Keys) { - if (!($lowerParamsToKeep.Contains($key.ToLower()))) + if (!($ParamsToKeep.Contains($key.ToLower()))) { $ParamsToRemove += $key } } } - if ($null -ne $ParamsToRemove -and $ParamsToRemove.Count -gt 0) + if ($ParamsToRemove.Count -gt 0) { foreach ($param in $ParamsToRemove) { - $PSBoundParametersIn.Remove($param) | Out-Null + $null = $PSBoundParametersIn.Remove($param) } } } @@ -612,5 +960,4 @@ function Get-OSEdition (Get-ItemProperty -Path 'HKLM:/software/microsoft/windows nt/currentversion' -Name InstallationType).InstallationType } - Export-ModuleMember -Function * diff --git a/README.md b/README.md index 290c9e7..48e01bd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# xBitlocker +# xBitlocker The **xBitlocker** module is a part of the Windows PowerShell Desired State Configuration (DSC) Resource Kit, which is a collection of DSC Resources produced by the PowerShell Team. This module contains the **xBLAutoBitlocker, xBLBitlocker, xBLTpm** resources. @@ -124,6 +124,9 @@ Defaults to false. * Update appveyor.yml to use the default template. * Added default template files .gitattributes, and .vscode settings. * Fixes most PSScriptAnalyzer issues. +* Fix issue where AutoUnlock is not set if requested, if the disk was + originally encrypted and AutoUnlock was not used. +* Add remaining Unit Tests for xBitlockerCommon. ### 1.2.0.0 diff --git a/Tests/Unit/xBitlockerCommon.tests.ps1 b/Tests/Unit/xBitlockerCommon.tests.ps1 index 608e1e9..6d16d1f 100644 --- a/Tests/Unit/xBitlockerCommon.tests.ps1 +++ b/Tests/Unit/xBitlockerCommon.tests.ps1 @@ -100,6 +100,120 @@ try TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TPMProtector' -RecoveryPasswordProtector $true | Should -Be $false } } + + Context 'When TestBitlocker is called and Get-BitlockerVolume returns null' { + It 'Should return False' { + Mock -CommandName Get-BitLockerVolume -Verifiable + + TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' | Should -Be $false + } + } + + Context 'When TestBitlocker is called and Get-BitlockerVolume returns a volume with no key protectors' { + It 'Should return False' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { + return @{ + KeyProtector = $null + } + } + + TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' | Should -Be $false + } + } + + Context 'When TestBitlocker is called, AutoUnlock is requested on a non-OS disk, and AutoUnlock is not enabled' { + It 'Should return False' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { + return @{ + AutoUnlockEnabled = $false + VolumeType = 'Data' + KeyProtector = @('Protector1') + } + } + + TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -AutoUnlock $true | Should -Be $false + } + } + + $defaultBLV = @( + @{ + KeyProtector = @('Protector1') + } + ) + + $fakePin = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'fakepin', (New-Object -TypeName System.Security.SecureString) + + Context 'When TestBitlocker is called, a AdAccountOrGroupProtector protector is requested, and does not exist on the disk' { + It 'Should return False' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $defaultBLV } + Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } + + TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -AdAccountOrGroupProtector $true | Should -Be $false + } + } + + Context 'When TestBitlocker is called, a PasswordProtector protector is requested, and does not exist on the disk' { + It 'Should return False' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $defaultBLV } + Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } + + TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -PasswordProtector $true | Should -Be $false + } + } + + Context 'When TestBitlocker is called, a Pin protector is requested, and does not exist on the disk' { + It 'Should return False' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $defaultBLV } + Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } + + TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -Pin $fakePin | Should -Be $false + } + } + + Context 'When TestBitlocker is called, a RecoveryKeyProtector protector is requested, and does not exist on the disk' { + It 'Should return False' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $defaultBLV } + Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } + + TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -RecoveryKeyProtector $true | Should -Be $false + } + } + + Context 'When TestBitlocker is called, a RecoveryPasswordProtector protector is requested, and does not exist on the disk' { + It 'Should return False' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $defaultBLV } + Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } + + TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -RecoveryPasswordProtector $true | Should -Be $false + } + } + + Context 'When TestBitlocker is called, a StartupKeyProtector protector is requested without a primary TPM protector, and does not exist on the disk' { + It 'Should return False' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $defaultBLV } + Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } + + TestBitlocker -MountPoint 'C:' -PrimaryProtector 'StartupKeyProtector' -StartupKeyProtector $true | Should -Be $false + } + } + + Context 'When TestBitlocker is called, a StartupKeyProtector protector is requested with a primary TPM protector, and does not exist on the disk' { + It 'Should return False' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $defaultBLV } + Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } + + TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -StartupKeyProtector $true | Should -Be $false + } + } + + Context 'When TestBitlocker is called, a TpmProtector protector is requested, and does not exist on the disk' { + It 'Should return False' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $defaultBLV } + Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } + + TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -TpmProtector $true | Should -Be $false + } + } } Describe 'xBitlockerCommon\CheckForPreReqs' { @@ -322,6 +436,488 @@ try {Get-OSEdition} | Should -Not -Throw } } + + Describe 'xBitLockerCommon\EnableBitlocker' -Tag 'Helper' { + # Override Bitlocker cmdlets + function Enable-Bitlocker {} + function Enable-BitlockerAutoUnlock {} + + AfterEach { + Assert-VerifiableMock + } + + $fakePin = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'fakepin', (New-Object -TypeName System.Security.SecureString) + $mountPoint = 'C:' + $encryptedBLV = @{ + VolumeStatus = 'FullyEncrypted' + } + $encryptedOSBLV = @{ + VolumeStatus = 'FullyEncrypted' + VolumeType = 'OperatingSystem' + } + $decryptedOSBLV = @{ + VolumeStatus = 'FullyDecrypted' + VolumeType = 'OperatingSystem' + } + + Context 'When EnableBitlocker is called Get-BitlockerVolume returns null' { + It 'Should throw an exception' { + Mock -CommandName Get-BitLockerVolume -Verifiable + + { EnableBitlocker -MountPoint 'C:' -Pin $fakePin -PrimaryProtector 'PasswordProtector' } | Should -Throw -ExpectedMessage 'Unable to find Bitlocker Volume associated with Mount Point' + } + } + + Context 'When EnableBitlocker is called with TpmProtector set to True and PrimaryProtector not set to TpmProtector' { + $badPrimaryProtectorCases = @( + @{ + PrimaryProtector = 'PasswordProtector' + } + + @{ + PrimaryProtector = 'RecoveryPasswordProtector' + } + + @{ + PrimaryProtector = 'StartupKeyProtector' + } + ) + + It 'Should throw an exception' -TestCases $badPrimaryProtectorCases { + param + ( + [System.String] + $PrimaryProtector + ) + + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $encryptedBLV } + + { EnableBitlocker -MountPoint $mountPoint -TpmProtector $true -PrimaryProtector $PrimaryProtector } | Should -Throw -ExpectedMessage 'If TpmProtector is used, it must be the PrimaryProtector.' + } + } + + Context 'When EnableBitlocker is called with Pin specified and TpmProtector not specified' { + It 'Should throw an exception' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $encryptedBLV } + + { EnableBitlocker -MountPoint $mountPoint -Pin $fakePin -PrimaryProtector 'PasswordProtector' } | Should -Throw -ExpectedMessage 'A TpmProtector must be used if Pin is used.' + } + } + + Context 'When EnableBitlocker is called with Pin specified and TpmProtector not specified' { + It 'Should throw an exception' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $encryptedBLV } + + { EnableBitlocker -MountPoint $mountPoint -Pin $fakePin -PrimaryProtector 'PasswordProtector' } | Should -Throw -ExpectedMessage 'A TpmProtector must be used if Pin is used.' + } + } + + $defaultEnableParams = @{ + MountPoint = $mountPoint + Pin = $fakePin + PrimaryProtector = 'TpmProtector' + TpmProtector = $true + EncryptionMethod = 'Aes256' + HardwareEncryption = $true + Service = $true + SkipHardwareTest = $true + UsedSpaceOnly = $true + AllowImmediateReboot = $true + StartupKeyProtector = $true + } + + Context 'When EnableBitlocker is called and the volume is not yet encrypted' { + It 'Should enable Bitlocker with the correct key protectors and parameters' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $decryptedOSBLV } + Mock -CommandName Enable-Bitlocker -Verifiable -MockWith { return $encryptedOSBLV } + Mock -CommandName Start-Sleep -Verifiable + Mock -CommandName Restart-Computer -Verifiable + + EnableBitlocker @defaultEnableParams + } + } + + Context 'When EnableBitlocker is called, the volume is not yet encrypted, and Enable-Bitlocker does not return a result' { + It 'Should throw an exception' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $decryptedOSBLV } + Mock -CommandName Enable-Bitlocker -Verifiable + + { EnableBitlocker @defaultEnableParams } | Should -Throw -ExpectedMessage 'Failed to successfully enable Bitlocker on MountPoint' + } + } + + Context 'When EnableBitlocker is called, the volume is not yet encrypted and is not an OS drive, and AutoUnlock is specified' { + It 'Should enable Bitlocker with the correct key protectors and parameters and enable AutoUnlock' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { + return @{ + VolumeStatus = 'FullyDecrypted' + VolumeType = 'Data' + } + } + Mock -CommandName Enable-Bitlocker -Verifiable -MockWith { return $encryptedBLV } + Mock -CommandName Enable-BitlockerAutoUnlock -Verifiable + + $defaultEnableParams.Add('AutoUnlock', $true) + + EnableBitlocker @defaultEnableParams + + $defaultEnableParams.Remove('AutoUnlock') + } + } + + Context 'When EnableBitlocker is called with TPM only and the volume is not yet encrypted' { + It 'Should enable Bitlocker with the correct key protectors and parameters' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $decryptedOSBLV } + Mock -CommandName Enable-Bitlocker -Verifiable -MockWith { return $encryptedBLV } + + $tpmOnlyEnableParams = @{ + MountPoint = $mountPoint + PrimaryProtector = 'TpmProtector' + TpmProtector = $true + } + + EnableBitlocker @tpmOnlyEnableParams + } + } + + Context 'When EnableBitlocker is called with TPM and pin only and the volume is not yet encrypted' { + It 'Should enable Bitlocker with the correct key protectors and parameters' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $decryptedOSBLV } + Mock -CommandName Enable-Bitlocker -Verifiable -MockWith { return $encryptedBLV } + + $tpmAndPinOnlyEnableParams = @{ + MountPoint = $mountPoint + PrimaryProtector = 'TpmProtector' + TpmProtector = $true + Pin = $fakePin + } + + EnableBitlocker @tpmAndPinOnlyEnableParams + } + } + + Context 'When EnableBitlocker is called with TPM and pin only and the volume is not yet encrypted' { + It 'Should enable Bitlocker with the correct key protectors and parameters' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $decryptedOSBLV } + Mock -CommandName Enable-Bitlocker -Verifiable -MockWith { return $encryptedBLV } + + $tpmAndStartupOnlyEnableParams = @{ + MountPoint = $mountPoint + PrimaryProtector = 'TpmProtector' + TpmProtector = $true + StartupKeyProtector = $true + StartupKeyPath = 'C:\' + } + + EnableBitlocker @tpmAndStartupOnlyEnableParams + } + } + + Context 'When EnableBitlocker is called with a Password Protector and the volume is not yet encrypted' { + It 'Should enable Bitlocker with the correct key protectors and parameters' { + Mock -CommandName Get-BitLockerVolume -MockWith { return $decryptedOSBLV } + Mock -CommandName Enable-Bitlocker -MockWith { return $encryptedBLV } + + $passwordEnableParams = @{ + MountPoint = $mountPoint + PrimaryProtector = 'PasswordProtector' + PasswordProtector = $true + Password = $fakePin + } + + EnableBitlocker @passwordEnableParams + } + } + + Context 'When EnableBitlocker is called with a Recovery Password Protector and the volume is not yet encrypted' { + It 'Should enable Bitlocker with the correct key protectors and parameters' { + Mock -CommandName Get-BitLockerVolume -MockWith { return $decryptedOSBLV } + Mock -CommandName Enable-Bitlocker -MockWith { return $encryptedBLV } + + $recoveryPasswordEnableParams = @{ + MountPoint = $mountPoint + PrimaryProtector = 'RecoveryPasswordProtector' + RecoveryPasswordProtector = $true + Password = $fakePin + } + + EnableBitlocker @recoveryPasswordEnableParams + } + } + + Context 'When EnableBitlocker is called with a StartupKey Protector and the volume is not yet encrypted' { + It 'Should enable Bitlocker with the correct key protectors and parameters' { + Mock -CommandName Get-BitLockerVolume -MockWith { return $decryptedOSBLV } + Mock -CommandName Enable-Bitlocker -MockWith { return $encryptedBLV } + + $startupKeyEnableParams = @{ + MountPoint = $mountPoint + PrimaryProtector = 'StartupKeyProtector' + StartupKeyProtector = $true + StartupKeyPath = 'C:\Path' + } + + EnableBitlocker @startupKeyEnableParams + } + } + } + + Describe 'xBitLockerCommon\Add-MissingBitLockerKeyProtector' -Tag 'Helper' { + # Override Bitlocker cmdlets + function Add-BitLockerKeyProtector {} + + # Suppress Write-Verbose output + Mock -CommandName Write-Verbose + + AfterEach { + Assert-VerifiableMock + } + + $fakePin = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList 'fakepin', (New-Object -TypeName System.Security.SecureString) + $mountPoint = 'C:' + $encryptedBLV = @{ + VolumeStatus = 'FullyEncrypted' + } + + Context 'When Add-MissingBitLockerKeyProtector is called, the AdAccountOrGroupProtector protector is requested but not yet present on the volume, and is not the PrimaryKeyProtector' { + It 'Should add the AdAccountOrGroupProtector protector' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $encryptedBLV } + Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false} + Mock -CommandName Add-BitLockerKeyProtector -Verifiable -ParameterFilter {$MountPoint -eq 'AdAccountOrGroupProtector'} + + EnableBitlocker -MountPoint 'AdAccountOrGroupProtector' -Pin $fakePin -PrimaryProtector 'TpmProtector' -TpmProtector $true -AdAccountOrGroupProtector $true + } + } + + Context 'When Add-MissingBitLockerKeyProtector is called, the PasswordProtector protector is requested but not yet present on the volume, and is not the PrimaryKeyProtector' { + It 'Should add the PasswordProtector protector' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $encryptedBLV } + Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false} + Mock -CommandName Add-BitLockerKeyProtector -Verifiable -ParameterFilter {$MountPoint -eq 'PasswordProtector'} + + EnableBitlocker -MountPoint 'PasswordProtector' -Pin $fakePin -PrimaryProtector 'TpmProtector' -TpmProtector $true -PasswordProtector $true + } + } + + Context 'When Add-MissingBitLockerKeyProtector is called, the RecoveryKeyProtector protector is requested but not yet present on the volume, and is not the PrimaryKeyProtector' { + It 'Should add the RecoveryKeyProtector protector' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $encryptedBLV } + Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false} + Mock -CommandName Add-BitLockerKeyProtector -Verifiable -ParameterFilter {$MountPoint -eq 'RecoveryKeyProtector'} + + EnableBitlocker -MountPoint 'RecoveryKeyProtector' -Pin $fakePin -PrimaryProtector 'TpmProtector' -TpmProtector $true -RecoveryKeyProtector $true + } + } + + Context 'When Add-MissingBitLockerKeyProtector is called, the RecoveryPasswordProtector protector is requested but not yet present on the volume, and is not the PrimaryKeyProtector' { + It 'Should add the RecoveryPasswordProtector protector' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $encryptedBLV } + Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false} + Mock -CommandName Add-BitLockerKeyProtector -Verifiable -ParameterFilter {$MountPoint -eq 'RecoveryPasswordProtector'} + + EnableBitlocker -MountPoint 'RecoveryPasswordProtector' -Pin $fakePin -PrimaryProtector 'TpmProtector' -TpmProtector $true -RecoveryPasswordProtector $true + } + } + + Context 'When Add-MissingBitLockerKeyProtector is called, the StartupKeyProtector protector is requested but not yet present on the volume, and is not the PrimaryKeyProtector' { + It 'Should add the StartupKeyProtector protector' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $encryptedBLV } + Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false} + Mock -CommandName Add-BitLockerKeyProtector -Verifiable -ParameterFilter {$MountPoint -eq 'StartupKeyProtector'} + + EnableBitlocker -MountPoint 'StartupKeyProtector' -PrimaryProtector 'RecoveryPasswordProtector' -RecoveryPasswordProtector $true -StartupKeyProtector $true + } + } + } + + <# + Describe 'xBitLockerCommon\TestBitlocker' -Tag 'Helper' { + # Override Bitlocker cmdlets + function Get-BitLockerVolume {} + + $mountPoint = 'C:' + + Context 'When TestBitlocker is called and Get-BitlockerVolume returns null' { + It 'Should return False' { + Mock -CommandName Get-BitLockerVolume -Verifiable + + TestBitlocker -MountPoint $mountPoint -PrimaryProtector 'TpmProtector' | Should -Be $false + } + } + + Context 'When TestBitlocker is called and the VolumeStatus is FullyDecrypted' { + It 'Should return False' { + Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { + return @{ + VolumeStatus = 'FullyDecrypted' + } + } + + TestBitlocker -MountPoint $mountPoint -PrimaryProtector 'TpmProtector' | Should -Be $false + } + } + } + #> + + Describe 'xBitLockerCommon\ContainsKeyProtector' -Tag 'Helper' { + $testKeyProtectorCollection = @( + @{ + KeyProtectorType = 'RecoveryPassword' + } + + @{ + KeyProtectorType = 'AdAccountOrGroup' + } + + @{ + KeyProtectorType = 'StartupKeyProtector' + } + ) + + Context 'When ContainsKeyProtector is called and the target KeyProtector exists in the collection' { + It 'Should return True' { + ContainsKeyProtector -Type 'AdAccountOrGroup' -KeyProtectorCollection $testKeyProtectorCollection | Should -Be $true + } + } + + Context 'When ContainsKeyProtector is called and the target KeyProtector does not exist in the collection' { + It 'Should return False' { + ContainsKeyProtector -Type 'AdAccountOrGroup2' -KeyProtectorCollection $testKeyProtectorCollection | Should -Be $false + } + } + + Context 'When ContainsKeyProtector is called with the StartsWith switch and the target KeyProtector exists in the collection' { + It 'Should return True' { + ContainsKeyProtector -Type 'AdAccount' -KeyProtectorCollection $testKeyProtectorCollection -StartsWith $true | Should -Be $true + } + } + + Context 'When ContainsKeyProtector is called with the StartsWith switch and the target KeyProtector does not exist in the collection' { + It 'Should return False' { + ContainsKeyProtector -Type 'Account' -KeyProtectorCollection $testKeyProtectorCollection -StartsWith $true | Should -Be $false + } + } + + Context 'When ContainsKeyProtector is called with the Contains switch and the target KeyProtector exists in the collection' { + It 'Should return True' { + ContainsKeyProtector -Type 'Account' -KeyProtectorCollection $testKeyProtectorCollection -Contains $true | Should -Be $true + } + } + + Context 'When ContainsKeyProtector is called with the Contains switch and the target KeyProtector does not exist in the collection' { + It 'Should return False' { + ContainsKeyProtector -Type 'NotInCollection' -KeyProtectorCollection $testKeyProtectorCollection -Contains $true | Should -Be $false + } + } + } + + Describe 'xBitLockerCommon\Convert-StringArrayToLowerCase' -Tag 'Helper' { + Context 'When Convert-StringArrayToLowerCase is called' { + It 'All input array members show be converted to lower case, and null members should be converted to empty strings' { + [System.String[]] $inputArray = @('ABC', 'dEf', $null, 'GhI', 'jkl', '', 'mnO') + + $outputArray = Convert-StringArrayToLowerCase -Array $inputArray + + $outputArray.Count | Should -Be 7 + $outputArray.Contains('abc') | Should -Be $true + $outputArray.Contains('def') | Should -Be $true + $outputArray.Contains('ghi') | Should -Be $true + $outputArray.Contains('jkl') | Should -Be $true + $outputArray.Contains('mno') | Should -Be $true + + $outputArray[2] | Should -Be '' + $outputArray[5] | Should -Be '' + } + } + } + + Describe 'xBitLockerCommon\AddParameters' -Tag 'Helper' { + AfterEach { + Assert-VerifiableMock + } + + Context 'When AddParameters is called, a parameter is added, and a parameter is changed' { + It 'Should add a new parameter and change the existing parameter' { + $param1 = 'abc' + $param2 = $null + $param2new = 'notnull' + $param3 = 'def' + $param4 = 'ghi' + + $psBoundParametersIn = @{ + Param1 = $param1 + Param2 = $param2 + Param3 = $param3 + } + + $paramsToAdd = @{ + Param2 = $param2new + Param4 = $param4 + } + + AddParameters -PSBoundParametersIn $psBoundParametersIn -ParamsToAdd $paramsToAdd + + $psBoundParametersIn.ContainsKey('Param1') -and $psBoundParametersIn['Param1'] -eq $param1 | Should -Be $true + $psBoundParametersIn.ContainsKey('Param2') -and $psBoundParametersIn['Param2'] -eq $param2new | Should -Be $true + $psBoundParametersIn.ContainsKey('Param3') -and $psBoundParametersIn['Param3'] -eq $param3 | Should -Be $true + $psBoundParametersIn.ContainsKey('Param4') -and $psBoundParametersIn['Param4'] -eq $param4 | Should -Be $true + } + } + } + + Describe 'xBitLockerCommon\RemoveParameters' -Tag 'Helper' { + AfterEach { + Assert-VerifiableMock + } + + Context 'When RemoveParameters is called and both ParamsToKeep and ParamsToRemove are specified' { + It 'Should throw an exception' { + { RemoveParameters -PSBoundParametersIn @{} -ParamsToKeep @('Param1') -ParamsToRemove @('Param2') } | ` + Should -Throw -ExpectedMessage 'Remove-FromPSBoundParametersUsingHashtable does not support using both ParamsToKeep and ParamsToRemove' + } + } + + Context 'When RemoveParameters is called with ParamsToKeep' { + It 'Should remove any parameter not specified in ParamsToKeep' { + Mock -CommandName Convert-StringArrayToLowerCase -Verifiable -MockWith { return @('param1', 'param2') } + + $psBoundParametersIn = @{ + Param1 = 1 + Param2 = 2 + Param3 = 3 + } + + $paramsToKeep = @('Param1', 'Param2') + + RemoveParameters -PSBoundParametersIn $psBoundParametersIn -ParamsToKeep $paramsToKeep + + $psBoundParametersIn.ContainsKey('Param1') | Should -Be $true + $psBoundParametersIn.ContainsKey('Param2') | Should -Be $true + $psBoundParametersIn.ContainsKey('Param3') | Should -Be $false + } + } + + Context 'When RemoveParameters is called with ParamsToRemove' { + It 'Should remove any parameter specified in ParamsToRemove' { + $psBoundParametersIn = @{ + Param1 = 1 + Param2 = 2 + Param3 = 3 + } + + $paramsToRemove = @( + 'Param1', + 'param2' + ) + + RemoveParameters -PSBoundParametersIn $psBoundParametersIn -ParamsToRemove $paramsToRemove + + $psBoundParametersIn.ContainsKey('Param1') | Should -Be $false + $psBoundParametersIn.ContainsKey('Param2') | Should -Be $false + $psBoundParametersIn.ContainsKey('Param3') | Should -Be $true + } + } + } } } finally From 5673026977a797549c4f279a72117ab2a891263f Mon Sep 17 00:00:00 2001 From: Mike Hendrickson Date: Fri, 2 Nov 2018 10:27:51 -0700 Subject: [PATCH 3/7] Add remaining Unit tests for xBitlockerCommon.psm1 --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 48e01bd..04d5941 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# xBitlocker +# xBitlocker The **xBitlocker** module is a part of the Windows PowerShell Desired State Configuration (DSC) Resource Kit, which is a collection of DSC Resources produced by the PowerShell Team. This module contains the **xBLAutoBitlocker, xBLBitlocker, xBLTpm** resources. @@ -114,7 +114,6 @@ Defaults to false. * *Identity:Not actually used, so could be anything * AllowClear:Indicates that the provisioning process clears the TPM, if necessary, to move the TPM closer to complying with Windows Server 2012 standards * AllowPhysicalPresence:Indicates that the provisioning process may send physical presence commands that require a user to be present in order to continue. - * AllowImmediateReboot:Whether the computer can rebooted immediately after initializing the TPM ## Versions From e1a9dbf10d69a52f1ac89b6fabfd499b74e91c46 Mon Sep 17 00:00:00 2001 From: Mike Hendrickson Date: Tue, 6 Nov 2018 14:06:59 -0800 Subject: [PATCH 4/7] Add remaining Unit tests for xBitlockerCommon.psm1 - Post Review #1 --- Tests/Unit/xBitlockerCommon.tests.ps1 | 33 +-------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/Tests/Unit/xBitlockerCommon.tests.ps1 b/Tests/Unit/xBitlockerCommon.tests.ps1 index 6d16d1f..0d75144 100644 --- a/Tests/Unit/xBitlockerCommon.tests.ps1 +++ b/Tests/Unit/xBitlockerCommon.tests.ps1 @@ -730,35 +730,6 @@ try } } - <# - Describe 'xBitLockerCommon\TestBitlocker' -Tag 'Helper' { - # Override Bitlocker cmdlets - function Get-BitLockerVolume {} - - $mountPoint = 'C:' - - Context 'When TestBitlocker is called and Get-BitlockerVolume returns null' { - It 'Should return False' { - Mock -CommandName Get-BitLockerVolume -Verifiable - - TestBitlocker -MountPoint $mountPoint -PrimaryProtector 'TpmProtector' | Should -Be $false - } - } - - Context 'When TestBitlocker is called and the VolumeStatus is FullyDecrypted' { - It 'Should return False' { - Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { - return @{ - VolumeStatus = 'FullyDecrypted' - } - } - - TestBitlocker -MountPoint $mountPoint -PrimaryProtector 'TpmProtector' | Should -Be $false - } - } - } - #> - Describe 'xBitLockerCommon\ContainsKeyProtector' -Tag 'Helper' { $testKeyProtectorCollection = @( @{ @@ -873,14 +844,12 @@ try Context 'When RemoveParameters is called and both ParamsToKeep and ParamsToRemove are specified' { It 'Should throw an exception' { { RemoveParameters -PSBoundParametersIn @{} -ParamsToKeep @('Param1') -ParamsToRemove @('Param2') } | ` - Should -Throw -ExpectedMessage 'Remove-FromPSBoundParametersUsingHashtable does not support using both ParamsToKeep and ParamsToRemove' + Should -Throw -ExpectedMessage 'Parameter set cannot be resolved using the specified named parameters.' } } Context 'When RemoveParameters is called with ParamsToKeep' { It 'Should remove any parameter not specified in ParamsToKeep' { - Mock -CommandName Convert-StringArrayToLowerCase -Verifiable -MockWith { return @('param1', 'param2') } - $psBoundParametersIn = @{ Param1 = 1 Param2 = 2 From f6af8895e1b03a8923795ea077d8e8d4d4a81332 Mon Sep 17 00:00:00 2001 From: Mike Hendrickson Date: Tue, 6 Nov 2018 14:12:22 -0800 Subject: [PATCH 5/7] Add remaining Unit tests for xBitlockerCommon.psm1 - Post Review #1 --- Tests/Unit/xBitlockerCommon.tests.ps1 | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/Tests/Unit/xBitlockerCommon.tests.ps1 b/Tests/Unit/xBitlockerCommon.tests.ps1 index 0d75144..add5987 100644 --- a/Tests/Unit/xBitlockerCommon.tests.ps1 +++ b/Tests/Unit/xBitlockerCommon.tests.ps1 @@ -782,26 +782,6 @@ try } } - Describe 'xBitLockerCommon\Convert-StringArrayToLowerCase' -Tag 'Helper' { - Context 'When Convert-StringArrayToLowerCase is called' { - It 'All input array members show be converted to lower case, and null members should be converted to empty strings' { - [System.String[]] $inputArray = @('ABC', 'dEf', $null, 'GhI', 'jkl', '', 'mnO') - - $outputArray = Convert-StringArrayToLowerCase -Array $inputArray - - $outputArray.Count | Should -Be 7 - $outputArray.Contains('abc') | Should -Be $true - $outputArray.Contains('def') | Should -Be $true - $outputArray.Contains('ghi') | Should -Be $true - $outputArray.Contains('jkl') | Should -Be $true - $outputArray.Contains('mno') | Should -Be $true - - $outputArray[2] | Should -Be '' - $outputArray[5] | Should -Be '' - } - } - } - Describe 'xBitLockerCommon\AddParameters' -Tag 'Helper' { AfterEach { Assert-VerifiableMock From dc6abeb3ba8e855ab07ff8aa408182e87f6fd8df Mon Sep 17 00:00:00 2001 From: Mike Hendrickson Date: Thu, 8 Nov 2018 12:45:18 -0800 Subject: [PATCH 6/7] Add remaining Unit tests for xBitlockerCommon.psm1 - Post Review #1 - Post Rebase --- DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 b/DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 index 6078906..138e867 100644 --- a/DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 +++ b/DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 @@ -5,7 +5,7 @@ function Get-TargetResource [OutputType([System.Collections.Hashtable])] param ( - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [System.String] $Identity ) From 2a4d43d4608f814c90bbddc596e38b58f19021fd Mon Sep 17 00:00:00 2001 From: Mike Hendrickson Date: Thu, 8 Nov 2018 13:37:51 -0800 Subject: [PATCH 7/7] Add remaining Unit tests for xBitlockerCommon.psm1 - Post Review #1 - Post Rebase --- DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 | 1 - Misc/xBitlockerCommon.psm1 | 6 +-- Tests/Unit/xBitlockerCommon.tests.ps1 | 58 +++++++++++------------ 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 b/DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 index 138e867..ba203e3 100644 --- a/DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 +++ b/DSCResources/MSFT_xBLTpm/MSFT_xBLTpm.psm1 @@ -118,7 +118,6 @@ function Test-TargetResource $tpm = Get-Tpm - if ($null -eq $tpm) { return $false diff --git a/Misc/xBitlockerCommon.psm1 b/Misc/xBitlockerCommon.psm1 index 12d87c6..53d7843 100644 --- a/Misc/xBitlockerCommon.psm1 +++ b/Misc/xBitlockerCommon.psm1 @@ -819,14 +819,14 @@ function ContainsKeyProtector [Parameter()] [string] $Type, - + [Parameter()] $KeyProtectorCollection, - + [Parameter()] [bool] $StartsWith = $false, - + [Parameter()] [bool] $Contains = $false diff --git a/Tests/Unit/xBitlockerCommon.tests.ps1 b/Tests/Unit/xBitlockerCommon.tests.ps1 index add5987..0d5845c 100644 --- a/Tests/Unit/xBitlockerCommon.tests.ps1 +++ b/Tests/Unit/xBitlockerCommon.tests.ps1 @@ -106,7 +106,7 @@ try Mock -CommandName Get-BitLockerVolume -Verifiable TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' | Should -Be $false - } + } } Context 'When TestBitlocker is called and Get-BitlockerVolume returns a volume with no key protectors' { @@ -118,7 +118,7 @@ try } TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' | Should -Be $false - } + } } Context 'When TestBitlocker is called, AutoUnlock is requested on a non-OS disk, and AutoUnlock is not enabled' { @@ -132,7 +132,7 @@ try } TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -AutoUnlock $true | Should -Be $false - } + } } $defaultBLV = @( @@ -149,7 +149,7 @@ try Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -AdAccountOrGroupProtector $true | Should -Be $false - } + } } Context 'When TestBitlocker is called, a PasswordProtector protector is requested, and does not exist on the disk' { @@ -158,7 +158,7 @@ try Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -PasswordProtector $true | Should -Be $false - } + } } Context 'When TestBitlocker is called, a Pin protector is requested, and does not exist on the disk' { @@ -167,7 +167,7 @@ try Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -Pin $fakePin | Should -Be $false - } + } } Context 'When TestBitlocker is called, a RecoveryKeyProtector protector is requested, and does not exist on the disk' { @@ -176,7 +176,7 @@ try Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -RecoveryKeyProtector $true | Should -Be $false - } + } } Context 'When TestBitlocker is called, a RecoveryPasswordProtector protector is requested, and does not exist on the disk' { @@ -185,7 +185,7 @@ try Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -RecoveryPasswordProtector $true | Should -Be $false - } + } } Context 'When TestBitlocker is called, a StartupKeyProtector protector is requested without a primary TPM protector, and does not exist on the disk' { @@ -194,7 +194,7 @@ try Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } TestBitlocker -MountPoint 'C:' -PrimaryProtector 'StartupKeyProtector' -StartupKeyProtector $true | Should -Be $false - } + } } Context 'When TestBitlocker is called, a StartupKeyProtector protector is requested with a primary TPM protector, and does not exist on the disk' { @@ -203,7 +203,7 @@ try Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -StartupKeyProtector $true | Should -Be $false - } + } } Context 'When TestBitlocker is called, a TpmProtector protector is requested, and does not exist on the disk' { @@ -212,7 +212,7 @@ try Mock -CommandName ContainsKeyProtector -Verifiable -MockWith { return $false } TestBitlocker -MountPoint 'C:' -PrimaryProtector 'TpmProtector' -TpmProtector $true | Should -Be $false - } + } } } @@ -465,7 +465,7 @@ try Mock -CommandName Get-BitLockerVolume -Verifiable { EnableBitlocker -MountPoint 'C:' -Pin $fakePin -PrimaryProtector 'PasswordProtector' } | Should -Throw -ExpectedMessage 'Unable to find Bitlocker Volume associated with Mount Point' - } + } } Context 'When EnableBitlocker is called with TpmProtector set to True and PrimaryProtector not set to TpmProtector' { @@ -493,7 +493,7 @@ try Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $encryptedBLV } { EnableBitlocker -MountPoint $mountPoint -TpmProtector $true -PrimaryProtector $PrimaryProtector } | Should -Throw -ExpectedMessage 'If TpmProtector is used, it must be the PrimaryProtector.' - } + } } Context 'When EnableBitlocker is called with Pin specified and TpmProtector not specified' { @@ -501,7 +501,7 @@ try Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $encryptedBLV } { EnableBitlocker -MountPoint $mountPoint -Pin $fakePin -PrimaryProtector 'PasswordProtector' } | Should -Throw -ExpectedMessage 'A TpmProtector must be used if Pin is used.' - } + } } Context 'When EnableBitlocker is called with Pin specified and TpmProtector not specified' { @@ -509,7 +509,7 @@ try Mock -CommandName Get-BitLockerVolume -Verifiable -MockWith { return $encryptedBLV } { EnableBitlocker -MountPoint $mountPoint -Pin $fakePin -PrimaryProtector 'PasswordProtector' } | Should -Throw -ExpectedMessage 'A TpmProtector must be used if Pin is used.' - } + } } $defaultEnableParams = @{ @@ -534,7 +534,7 @@ try Mock -CommandName Restart-Computer -Verifiable EnableBitlocker @defaultEnableParams - } + } } Context 'When EnableBitlocker is called, the volume is not yet encrypted, and Enable-Bitlocker does not return a result' { @@ -543,7 +543,7 @@ try Mock -CommandName Enable-Bitlocker -Verifiable { EnableBitlocker @defaultEnableParams } | Should -Throw -ExpectedMessage 'Failed to successfully enable Bitlocker on MountPoint' - } + } } Context 'When EnableBitlocker is called, the volume is not yet encrypted and is not an OS drive, and AutoUnlock is specified' { @@ -562,7 +562,7 @@ try EnableBitlocker @defaultEnableParams $defaultEnableParams.Remove('AutoUnlock') - } + } } Context 'When EnableBitlocker is called with TPM only and the volume is not yet encrypted' { @@ -577,7 +577,7 @@ try } EnableBitlocker @tpmOnlyEnableParams - } + } } Context 'When EnableBitlocker is called with TPM and pin only and the volume is not yet encrypted' { @@ -593,7 +593,7 @@ try } EnableBitlocker @tpmAndPinOnlyEnableParams - } + } } Context 'When EnableBitlocker is called with TPM and pin only and the volume is not yet encrypted' { @@ -610,7 +610,7 @@ try } EnableBitlocker @tpmAndStartupOnlyEnableParams - } + } } Context 'When EnableBitlocker is called with a Password Protector and the volume is not yet encrypted' { @@ -626,7 +626,7 @@ try } EnableBitlocker @passwordEnableParams - } + } } Context 'When EnableBitlocker is called with a Recovery Password Protector and the volume is not yet encrypted' { @@ -642,7 +642,7 @@ try } EnableBitlocker @recoveryPasswordEnableParams - } + } } Context 'When EnableBitlocker is called with a StartupKey Protector and the volume is not yet encrypted' { @@ -658,7 +658,7 @@ try } EnableBitlocker @startupKeyEnableParams - } + } } } @@ -686,7 +686,7 @@ try Mock -CommandName Add-BitLockerKeyProtector -Verifiable -ParameterFilter {$MountPoint -eq 'AdAccountOrGroupProtector'} EnableBitlocker -MountPoint 'AdAccountOrGroupProtector' -Pin $fakePin -PrimaryProtector 'TpmProtector' -TpmProtector $true -AdAccountOrGroupProtector $true - } + } } Context 'When Add-MissingBitLockerKeyProtector is called, the PasswordProtector protector is requested but not yet present on the volume, and is not the PrimaryKeyProtector' { @@ -696,7 +696,7 @@ try Mock -CommandName Add-BitLockerKeyProtector -Verifiable -ParameterFilter {$MountPoint -eq 'PasswordProtector'} EnableBitlocker -MountPoint 'PasswordProtector' -Pin $fakePin -PrimaryProtector 'TpmProtector' -TpmProtector $true -PasswordProtector $true - } + } } Context 'When Add-MissingBitLockerKeyProtector is called, the RecoveryKeyProtector protector is requested but not yet present on the volume, and is not the PrimaryKeyProtector' { @@ -706,7 +706,7 @@ try Mock -CommandName Add-BitLockerKeyProtector -Verifiable -ParameterFilter {$MountPoint -eq 'RecoveryKeyProtector'} EnableBitlocker -MountPoint 'RecoveryKeyProtector' -Pin $fakePin -PrimaryProtector 'TpmProtector' -TpmProtector $true -RecoveryKeyProtector $true - } + } } Context 'When Add-MissingBitLockerKeyProtector is called, the RecoveryPasswordProtector protector is requested but not yet present on the volume, and is not the PrimaryKeyProtector' { @@ -716,7 +716,7 @@ try Mock -CommandName Add-BitLockerKeyProtector -Verifiable -ParameterFilter {$MountPoint -eq 'RecoveryPasswordProtector'} EnableBitlocker -MountPoint 'RecoveryPasswordProtector' -Pin $fakePin -PrimaryProtector 'TpmProtector' -TpmProtector $true -RecoveryPasswordProtector $true - } + } } Context 'When Add-MissingBitLockerKeyProtector is called, the StartupKeyProtector protector is requested but not yet present on the volume, and is not the PrimaryKeyProtector' { @@ -726,7 +726,7 @@ try Mock -CommandName Add-BitLockerKeyProtector -Verifiable -ParameterFilter {$MountPoint -eq 'StartupKeyProtector'} EnableBitlocker -MountPoint 'StartupKeyProtector' -PrimaryProtector 'RecoveryPasswordProtector' -RecoveryPasswordProtector $true -StartupKeyProtector $true - } + } } }