PowerShell

Backing up Bitlocker Keys and LAPS passwords from Active Directory

Having a modern, secure infrastructure in 2019 is a requirement. You should implement BitLocker to make sure that in the event of stolen laptop data is not readily extractable and implementing LAPS is a must in a fast changing IT world. But I'm not here to convince you to those two security features. I'm here to show you an easy way to backup LAPS and BitLocker. While having everything stored in Active Directory is excellent, things can get complicated when you don't have access to your Active Directory, or you restore an older version of it. You see, LAPS, for example, keeps only last Administrator password. This is great and all but what happens if you restore the machine from backup from 6 months back? Your password has already changed multiple times. During our testing of DR scenarios, we wanted to access the computer via their local Administrator credentials and we just couldn't because that password was already gone.

Extracting Bitlocker and LAPS passwords is very risky
Please be aware that exporting Bitlocker and LAPS is considered a risk if you mishandle the data. You shouldn't extract that data unless you have a valid reason and you store that data safely somewhere in a safe place. Saving it on your file share isn't the best idea!
Extracting Bitlocker from Active Directory

Below code should allow you to extract all BitLocker Keys available in Active Directory for all computers. Keep in mind it will get it all. Not only the latest one.

function Get-WinADDomainBitlocker {
    param(
        [string] $Domain = $Env:USERDNSDOMAIN,
        [Array] $Computers
    )
    $Properties = @(
        'Name',
        'OperatingSystem',
        'DistinguishedName'
    )
    #[DateTime] $CurrentDate = Get-Date

    if ($null -eq $Computers) {
        $Computers = Get-ADComputer -Filter * -Properties $Properties -Server $Domain
    }
    foreach ($Computer in $Computers) {
        $Bitlockers = Get-ADObject -Filter 'objectClass -eq "msFVE-RecoveryInformation"' -SearchBase $Computer.DistinguishedName -Properties 'WhenCreated', 'msFVE-RecoveryPassword' #|  Sort-Object whenCreated -Descending #| Select-Object whenCreated, msFVE-RecoveryPassword
        
        foreach ($Bitlocker in $Bitlockers) {
            [PSCustomObject] @{
                'Name'                        = $Computer.Name
                'Operating System'            = $Computer.'OperatingSystem'
                'Bitlocker Recovery Password' = $Bitlocker.'msFVE-RecoveryPassword'
                'Bitlocker When'              = $Bitlocker.WhenCreated
                'DistinguishedName'           = $Computer.'DistinguishedName'
            }
        }
    }
}

Usage is quite simple

$MyBitlocker = Get-WinADDomainBitlocker
$MyBitlocker | Format-Table -AutoSize


# Exporting to  Excel 
# Install-Module PSWriteExcel # is required
ConvertTo-Excel -FilePath 'C:\myFile.xlsx' -DataTable $MyBitlocker -AutoFilter -AutoFit -ExcelWorkSheetName 'Bitlocker Keys'
Extracting LAPS from Active Directory

Below code is able to deliver extracted LAPS passwords for each, and every computer in Active Directory. It also shows Expiration Time and Days to Expire.

function Get-WinADDomainLAPS {
    [CmdletBinding()]
    param(
        [string] $Domain = $Env:USERDNSDOMAIN,
        [Array] $Computers
    )
    $Properties = @(
        'Name',
        'OperatingSystem',
        'DistinguishedName',
        'ms-Mcs-AdmPwd',
        'ms-Mcs-AdmPwdExpirationTime'
    )
    [DateTime] $CurrentDate = Get-Date

    if ($null -eq $Computers -or $Computers.Count -eq 0) {
        $Computers = Get-ADComputer -Filter * -Properties $Properties
    }
    foreach ($Computer in $Computers) {
        [PSCustomObject] @{
            'Name'                 = $Computer.Name
            'Operating System'     = $Computer.'OperatingSystem'
            'Laps Password'        = $Computer.'ms-Mcs-AdmPwd'
            'Laps Expire (days)'   = Convert-TimeToDays -StartTime ($CurrentDate) -EndTime (Convert-ToDateTime -Timestring ($Computer.'ms-Mcs-AdmPwdExpirationTime'))
            'Laps Expiration Time' = Convert-ToDateTime -Timestring ($Computer.'ms-Mcs-AdmPwdExpirationTime')
            'DistinguishedName'    = $Computer.'DistinguishedName'
        }
    }
}

Again, usage is very similar to what you get in Bitlocker above

$Laps = Get-WinADDomainLAPS
$Laps | Format-Table -AutoSize

# Again using this required PSWriteExcel
ConvertTo-Excel -FilePath 'C:\myFile.xlsx' -DataTable $Laps -AutoFilter -AutoFit -ExcelWorkSheetName 'LAPS'
Required modules

There are two PowerShell modules I'm using that I've created and are utilized above. One is PSSharedGoods, and one is PSWriteExcel. PSSharedGoods is required for two functions Convert-TimeToDays and Convert-ToDateTime. If you don't feel like installing those feel free to use the code below. It's not strongly optimized, but it works. PSWriteExcel is responsible for creating an Excel file if you prefer to export using other, more common modules you don't need that.

function Convert-TimeToDays {
    [CmdletBinding()]
    param (
        $StartTime,
        $EndTime,
        #[nullable[DateTime]] $StartTime,
        #[nullable[DateTime]] $EndTime,
        [string] $Ignore = '*1601*'
    )
    if ($StartTime -and $EndTime) {
        try {
            if ($StartTime -notlike $Ignore -and $EndTime -notlike $Ignore) {
                $Days = (NEW-TIMESPAN -Start (GET-DATE) -End ($EndTime)).Days
            } else {
                $Days = $null
            }
        } catch {
            $Days = $null
        }
    }
    return $Days
}

function Convert-ToDateTime {
    [CmdletBinding()]
    param (
        [string] $Timestring,
        [string] $Ignore = '*1601*'
    )
    Try {
        $DateTime = ([datetime]::FromFileTime($Timestring))
    } catch {
        $DateTime = $null
    }
    if ($null -eq $DateTime -or $DateTime -like $Ignore) {
        return $null
    } else {
        return $DateTime
    }
}

This post was last modified on %s = human-readable time difference 00:12

Przemyslaw Klys

System Architect with over 14 years of experience in the IT field. Skilled, among others, in Active Directory, Microsoft Exchange and Office 365. Profoundly interested in PowerShell. Software geek.

Share
Published by
Przemyslaw Klys

Recent Posts

Upgrade Azure Active Directory Connect fails with unexpected error

Today, I made the decision to upgrade my test environment and update the version of…

1 month ago

Mastering Active Directory Hygiene: Automating Stale Computer Cleanup with CleanupMonster

Have you ever looked at your Active Directory and wondered, "Why do I still have…

2 months ago

Active Directory Replication Summary to your Email or Microsoft Teams

Active Directory replication is a critical process that ensures the consistent and up-to-date state of…

7 months ago

Syncing Global Address List (GAL) to personal contacts and between Office 365 tenants with PowerShell

Hey there! Today, I wanted to introduce you to one of the small but excellent…

11 months ago

Active Directory Health Check using Microsoft Entra Connect Health Service

Active Directory (AD) is crucial in managing identities and resources within an organization. Ensuring its…

1 year ago

Seamless HTML Report Creation: Harness the Power of Markdown with PSWriteHTML PowerShell Module

In today's digital age, the ability to create compelling and informative HTML reports and documents…

1 year ago