
Automate BitLocker Status Reporting Across a Fleet with PowerShell
Before you run this
This guide builds a read-only report: it collects the BitLocker encryption status of every fixed volume on a set of domain computers and writes it to a CSV. It does not enable, disable, or change BitLocker on anything. The only thing it creates on your side is the output file.
It still needs privileges. Get-BitLockerVolume returns full information only from an elevated context, and you are reaching each machine over WinRM, so you must run this as an account that is a local administrator on the target computers (typically a domain account in the local Administrators group via GPO). Pulling the computer list also uses the ActiveDirectory module (part of RSAT), so run it from an admin workstation or management server that has RSAT installed and an elevated PowerShell session.
Even though this is read-only, read the script before you run it and test it against one machine first — set your OU or target to a single test computer and confirm the output looks right before you point it at the whole fleet. A typo in the search base or a firewall blocking WinRM will just give you empty or error rows, not damage, but you want to see that behaviour on a small scale first.
There is nothing to roll back because nothing is changed on the endpoints. The only cleanup is deleting the CSV the script produces. If you later extend this into anything that changes BitLocker state, treat that as a different, destructive job and gate it accordingly.
Assumptions
I'm writing this for a domain environment:
- Targets are Windows 10/11 Pro or Enterprise, or Windows Server 2016+, all domain-joined. The BitLocker cmdlets are built in on these editions; Windows Home does not have them.
- WinRM is enabled on the targets (the domain default when you've pushed the "Allow remote server management through WinRM" GPO, or run
Enable-PSRemotingat build time). - You run the report from an admin workstation with PowerShell 5.1 and the ActiveDirectory RSAT module installed.
- The account you use has local admin rights on the targets.
If you don't use RSAT/AD to source your machine list, you can feed the list from a text file instead — I'll note that below.
The core cmdlet
The one that does the real work is Get-BitLockerVolume. Run locally on any one machine it returns an object per volume with the fields we care about:
Get-BitLockerVolume | Format-List MountPoint, VolumeStatus, ProtectionStatus, EncryptionPercentage, EncryptionMethod, KeyProtector
The useful properties:
VolumeStatus— e.g.FullyEncrypted,FullyDecrypted,EncryptionInProgress.ProtectionStatus—OnorOff. A volume can be fully encrypted but with protection suspended, so this matters.EncryptionPercentage— handy for spotting machines mid-encryption.EncryptionMethod— the cipher, e.g.XtsAes256.KeyProtector— a collection; each entry has aKeyProtectorTypesuch asTpmorRecoveryPassword.
Confirm those property names on your own build with the Format-List line above before trusting a large run — vendor cmdlets do add and rename fields between OS versions, and Microsoft Learn documents the current set under Get-BitLockerVolume (BitLocker module).
The fleet report script
Save this as Get-FleetBitLockerReport.ps1. Edit the three settings at the top; the OU, path, and the credential prompt are the only things you touch.
#Requires -Version 5.1
#Requires -Modules ActiveDirectory
# --- Settings you edit ---
$SearchBase = "OU=Workstations,DC=example,DC=com" # OU to pull computers from
$OutputCsv = "C:\path\to\BitLockerReport.csv" # where the report is written
$Credential = Get-Credential # account with local admin on the targets
# Pull enabled computer objects from that OU
$computers = Get-ADComputer -SearchBase $SearchBase -Filter 'Enabled -eq $true' |
Select-Object -ExpandProperty Name
$results = foreach ($name in $computers) {
# Skip anything not answering, so one dead host doesn't stall the run
if (-not (Test-Connection -ComputerName $name -Count 1 -Quiet)) {
[pscustomobject]@{
Computer = $name
MountPoint = $null
VolumeStatus = "Unreachable"
ProtectionStatus = $null
EncryptionPct = $null
EncryptionMethod = $null
HasRecoveryKey = $null
}
continue
}
try {
Invoke-Command -ComputerName $name -Credential $Credential -ErrorAction Stop -ScriptBlock {
Get-BitLockerVolume | ForEach-Object {
[pscustomobject]@{
Computer = $env:COMPUTERNAME
MountPoint = $_.MountPoint
VolumeStatus = $_.VolumeStatus
ProtectionStatus = $_.ProtectionStatus
EncryptionPct = $_.EncryptionPercentage
EncryptionMethod = $_.EncryptionMethod
# true if a recovery-password protector exists on the volume
HasRecoveryKey = [bool]($_.KeyProtector.KeyProtectorType -contains 'RecoveryPassword')
}
}
}
}
catch {
[pscustomobject]@{
Computer = $name
MountPoint = $null
VolumeStatus = "Error: $($_.Exception.Message)"
ProtectionStatus = $null
EncryptionPct = $null
EncryptionMethod = $null
HasRecoveryKey = $null
}
}
}
# Explicit Select keeps the CSV clean (Invoke-Command adds PSComputerName etc.)
$results |
Select-Object Computer, MountPoint, VolumeStatus, ProtectionStatus, EncryptionPct, EncryptionMethod, HasRecoveryKey |
Export-Csv -Path $OutputCsv -NoTypeInformation -Encoding UTF8
Replace OU=Workstations,DC=example,DC=com with your real OU distinguished name, and C:\path\to\BitLockerReport.csv with a real output path. Get-Credential will prompt you interactively — that's the safe default; don't hard-code a password in the file.
To test on one machine first, temporarily replace the Get-ADComputer line with a single name:
$computers = @("TESTPC01") # replace with a real test computer name
No AD? Same idea — feed the list from a file instead of Get-ADComputer:
$computers = Get-Content "C:\path\to\computers.txt"
The Unreachable and Error: rows are deliberate. A machine that's off, on VPN, or blocking WinRM should show up as a distinct state, not vanish — an unreported machine is exactly the one you can't afford to lose track of.
Running it on a schedule
To have this land in your inbox or a share weekly, register it as a scheduled task under a service account that has the necessary rights, and change the output path to a dated filename or a network share. Task Scheduler runs it non-interactively, so the interactive Get-Credential prompt won't work in that mode — you'd store the credential securely (for example with Export-Clixml of a PSCredential, which is tied to the account and machine that created it) and import it in the script. Check the exact Export-Clixml/Import-Clixml and Register-ScheduledTask usage on Microsoft Learn before wiring that up; get the report right by hand first.
For larger estates
If you have hundreds or thousands of endpoints, a pull-based script over WinRM gets slow and misses anything off the wire. The mainstream answers are agent- or cloud-based: Microsoft Intune reports encryption status centrally, and Microsoft Configuration Manager (SCCM) has BitLocker management built in. Recovery keys themselves, if you escrow them to AD, live on the computer object as msFVE-RecoveryInformation child objects. Those are worth naming so you know where to grow to; this script is the no-extra-infrastructure option.
Verify it worked
Open the CSV, or summarise it from PowerShell:
# Quick breakdown by protection status
Import-Csv "C:\path\to\BitLockerReport.csv" |
Group-Object ProtectionStatus |
Select-Object Name, Count
Sanity checks:
- The total row count roughly matches the number of machines you expected (allowing for multiple volumes per machine).
- Machines you know are encrypted show
ProtectionStatus = OnandVolumeStatus = FullyEncrypted. Unreachable/Error:rows are ones to chase — usually powered off, or WinRM not reachable. Confirm connectivity to one withTest-WSMan -ComputerName TESTPC01.
Undo
There's nothing to reverse on the endpoints — this only reads state. To clean up, delete the CSV and, if you scheduled it, remove the task and any stored credential file you created.
Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.
More about the author →Was this article helpful?
Tap a star — no sign-in needed.
Be the first to rate this article.
