As system administrators, security is a cornerstone of our responsibilities. One critical aspect of this is ensuring that sensitive data on workstations and servers is encrypted. Microsoft BitLocker provides robust encryption, but managing it at scale can be challenging. This script automates essential BitLocker-related tasks while ensuring the necessary prerequisites, such as a Trusted Platform Module (TPM) chip, are configured correctly. It also sets up a scheduled task to notify users of significant events without disrupting the user experience.
NOTE: Module BurntToast needs to be installed - Please look at Custom Notification Script Using BurntToast Module
1. Internal/External Network Detection
The script starts by checking if the client is on an internal network using the Test-Path
cmdlet
$LeaveMode = 'BREAK'
$Result = Test-Path 'SOME_INTERNAL_PATH'
if ($Result -eq $true) {
Write-Host 'Client is internal!' -ForegroundColor Cyan
} else {
Write-Host 'Client is not internal!' -ForegroundColor Magenta
[System.Environment]::Exit(0x00041300)
Invoke-Expression $LeaveMode
}
This ensures BitLocker operations are only performed on devices connected to a internal network. If the client is external, the script exits with a specific code (0x00041300
) and executes the specified leave mode, such as BREAK
– I use BREAK
for Development and EXIT
in Deployment.
2. Managing BitLocker Protection
The script evaluates BitLocker and TPM statuses using Get-BitLockerVolume
and Get-Tpm
$Bitlocker = Get-BitLockerVolume -MountPoint $env:SystemDrive
$TPM = Get-Tpm
Case 1: BitLocker is already active
If BitLocker protection is On
if ($Bitlocker.ProtectionStatus -eq 'On') {
Write-Host 'Bitlocker is activated! -> BREAK' -ForegroundColor Cyan
Set-ItemProperty -Path 'HKLM:\HARDWARE' -Name 'Bitlocker' -Value 1
Invoke-Expression $LeaveMode
}
This records the status in the registry and terminates further execution, confirming encryption is active.
The key HKLM:\HARDWARE
ensures the script doesn’t re-run unnecessarily on systems where it has already been applied. When deploying the script via GPO, this prevents redundant execution, optimizes system performance, and allows the scheduled task to be removed via GPO after execution
Case 2: TPM is present, but BitLocker isn’t fully active
If encryption is incomplete or disabled, the script:
- Disables existing BitLocker encryption.
- Waits until decryption finishes.
- Adds a TPM-based protector.
- Re-enables encryption in “UsedSpaceOnly” mode for speed.
- Backs up the key to Azure Active Directory (AAD).
Adding the TPM Protector
Add-BitLockerKeyProtector -MountPoint $env:SystemDrive -RecoveryPasswordProtector | Out-Null
Enabling Bitlocker
Enable-BitLocker -MountPoint $env:SystemDrive -TpmProtector -UsedSpaceOnly -ErrorAction Stop
Backing Up the Key
BackupToAAD-BitLockerKeyProtector -MountPoint $env:SystemDrive -KeyProtectorId $ProtectorID | Out-Null
If the BitLocker configuration is corrupted, the script renames the ReAgent.xml
file
Rename-Item -Path "C:\Windows\System32\Recovery\ReAgent.xml" -NewName "C:\Windows\System32\Recovery\ReAgent.old.xml"
Full Script
$LeaveMode = 'BREAK'
# Is Client internal?
$Result = Test-Path 'SOME_INTERNAL_PATH'
if ($Result -eq $true) {
Write-Host 'Client is internal!' -ForegroundColor Cyan
}
else {
Write-Host 'Client is not internal!' -ForegroundColor Magenta
[System.Environment]::Exit(0x00041300)
Invoke-Expression $LeaveMode
}
function Create-ScheduledTask{
$currentUser = (Get-CimInstance -ClassName win32_ComputerSystem | select UserName).userName
$currentDate = Get-Date
$currentDate = $currentDate.AddSeconds(10)
#$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-windowstyle hidden -executionpolicy bypass -noprofile -File *PATH_TO_SEND_TOAST_MESSAGE* -Restart"
# use cmd variant to really supress the gui. otherwise the will be a short cli windows visible, since the script needs to run in user context
$action = New-ScheduledTaskAction -Execute "cmd.exe" -Argument "/c start /min powershell.exe -windowstyle hidden -executionpolicy bypass -Command *PATH_TO_SEND_TOAST_MESSAGE* -Restart"
$trigger = New-ScheduledTaskTrigger -Once -At $currentDate
$principal = New-ScheduledTaskPrincipal -UserId $currentUser
$task = New-ScheduledTask -Action $action -Principal $principal -Trigger $trigger
Register-ScheduledTask 'Send-ToastNotification' -InputObject $task | Out-Null
# Edit Task and Set Auto Deletion
$createdTask = Get-ScheduledTask -TaskName 'Send-ToastNotification'
$createdTask.Author = "DOMAIN\USERNAME"
$createdTask.Triggers[0].StartBoundary = [DateTime]::Now.AddSeconds(15).ToString("yyyy-MM-dd'T'HH:mm:ss")
$createdTask.Triggers[0].EndBoundary = [DateTime]::Now.AddSeconds(50).ToString("yyyy-MM-dd'T'HH:mm:ss")
$createdTask.Settings.DeleteExpiredTaskAfter = "PT0S"
$createdTask | Set-ScheduledTask
}
$Bitlocker = Get-BitLockerVolume -MountPoint $env:SystemDrive
$TPM = Get-Tpm
# Bitlocker is working:
if ($Bitlocker.ProtectionStatus -eq 'On') {
Write-Host 'Bitlocker is activated! -> BREAK' -ForegroundColor Cyan
Set-ItemProperty -Path 'HKLM:\HARDWARE' -Name 'Bitlocker' -Value 1
Invoke-Expression $LeaveMode
}
# TPM is present, Volume isnt FullyEncrypted:
elseif ($TPM.TpmPresent -eq $true -and $Bitlocker.ProtectionStatus -eq 'off' -and $Bitlocker.VolumeStatus -ne 'EncryptionInProgress') {
# Disable present Bitlocker:
Disable-BitLocker -MountPoint $env:SystemDrive
do {
$EncryptionStatus = (Get-BitLockerVolume -MountPoint $env:SystemDrive).EncryptionPercentage
Write-Host 'EncryptionStatus:' $EncryptionStatus 'Percentage' -ForegroundColor Cyan
sleep -Milliseconds 2000
}
until($EncryptionStatus -eq 0)
# Add Bitlocker Protector with TPM
Add-BitLockerKeyProtector -MountPoint $env:SystemDrive -RecoveryPasswordProtector | Out-Null
# Get Protector ID:
$ProtectorID = Get-BitLockerVolume -MountPoint $env:SystemDrive | select -ExpandProperty KeyProtector
$ProtectorID = $ProtectorID.KeyProtectorId
try{
# Enable Bitlocker:
Enable-BitLocker -MountPoint $env:SystemDrive -TpmProtector -UsedSpaceOnly -ErrorAction Stop
# Backup ProtectorID in AD:
BackupToAAD-BitLockerKeyProtector -MountPoint $env:SystemDrive -KeyProtectorId $ProtectorID | Out-Null
Set-ItemProperty -Path 'HKLM:\HARDWARE' -Name 'Bitlocker' -Value 1
# CREATE SCHEDULED TASK TO SEND-TOASTMESSAGE
Create-ScheduledTask
Invoke-Expression $LeaveMode
}catch{
# Bitlocker Config corrupted
Write-Output 'ReAgent File is corrupted - Rename File and run script at next logon again'
Rename-Item -Path "C:\Windows\System32\Recovery\ReAgent.xml" -NewName "C:\Windows\System32\Recovery\ReAgent.old.xml"
Invoke-Expression $LeaveMode
}
}
else {
Write-Host 'No TPM Chip was found or Encryption is in progress - BREAK!' -ForegroundColor Red
Set-ItemProperty -Path 'HKLM:\HARDWARE' -Name 'Bitlocker' -Value 1
Invoke-Expression $LeaveMode
}