PowerShell

Getting Bitlocker and LAPS summary report with PowerShell

Having Bitlocker and LAPS in modern Active Directory is a must. But just because you enable GPO and have a process that should say Bitlocker and LAPS are enabled doesn't mean much. Now and then you should verify things yourself. One of the Facebook users on PowerShell group just had this idea of exporting Bitlocker keys and then giving that list to his colleagues for manual verification. He wanted to do it half PowerShell and half manually. While the idea was great, why not take full advantage of PowerShell and have a helpful report with all the necessary information?

Summary report for LAPS and Bitlocker and it's status in Active Directory

This script below is based on two of my earlier articles

I've already covered exporting LAPS passwords or Bitlocker keys. This one focuses on just getting a summary with that information for management visibility. Report contains: Name, Enabled, DNSHostName, DistinguishedName, System, LastLogonDate, Encrypted,EncryptedTime, Laps, LapsExpirationDays, LapsExpirationTime. This means that with just one little command, you get everything at once.

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
    }
}
function Get-WinADForestSchemaPropertiesComputers {
    [CmdletBinding()]
    param(
    )
    $Schema = [directoryservices.activedirectory.activedirectoryschema]::GetCurrentSchema()
    @(
        $Schema.FindClass("computer").mandatoryproperties | Select-Object name, commonname, description, syntax
        $Schema.FindClass("computer").optionalproperties | Select-Object name, commonname, description, syntax #| Where-Object { $_.Name -eq 'ms-Mcs-AdmPwd' } # ft -AutoSize
    )
}
function ConvertTo-OperatingSystem {
    [CmdletBinding()]
    param(
        [string] $OperatingSystem,
        [string] $OperatingSystemVersion
    )
    if ($OperatingSystem -like 'Windows 10*') {
        $Systems = @{
            '10.0 (18362)' = "Windows 10 1903"
            '10.0 (17763)' = "Windows 10 1809"
            '10.0 (17134)' = "Windows 10 1803"
            '10.0 (16299)' = "Windows 10 1709"
            '10.0 (15063)' = "Windows 10 1703"
            '10.0 (14393)' = "Windows 10 1607"
            '10.0 (10586)' = "Windows 10 1511"
            '10.0 (10240)' = "Windows 10 1507"
            '10.0 (18898)' = 'Windows 10 Insider Preview'
        }
        $System = $Systems[$OperatingSystemVersion]
    } elseif ($OperatingSystem -notlike 'Windows 10*') {
        $System = $OperatingSystem
    }
    if ($System) {
        $System
    } else {
        'Unknown'
    }
}
function Get-WinBitlockerAndLapsSummary {
    [CmdletBinding()]
    param(
    )
    $ComputerProperties = Get-WinADForestSchemaPropertiesComputers
    if ($ComputerProperties.Name -contains 'ms-Mcs-AdmPwd') {
        $LapsAvailable = $true
        $Properties = @(
            'Name'
            'OperatingSystem'
            'OperatingSystemVersion'
            'DistinguishedName'
            'LastLogonDate'
            'ms-Mcs-AdmPwd'
            'ms-Mcs-AdmPwdExpirationTime'
        )
    } else {
        $LapsAvailable = $false
        $Properties = @(
            'Name'
            'OperatingSystem'
            'OperatingSystemVersion'
            'DistinguishedName'
            'LastLogonDate'
        )
    }
    $CurrentDate = Get-Date
    $Computers = Get-ADComputer -Filter * -Properties $Properties
    $FormattedComputers = foreach ($_ in $Computers) {
        if ($LapsAvailable) {
            if ($_.'ms-Mcs-AdmPwd') {
                $Laps = $true
                $LapsExpirationDays = Convert-TimeToDays -StartTime ($CurrentDate) -EndTime (Convert-ToDateTime -Timestring ($_.'ms-Mcs-AdmPwdExpirationTime'))
                $LapsExpirationTime = Convert-ToDateTime -Timestring ($_.'ms-Mcs-AdmPwdExpirationTime')
            } else {
                $Laps = $false
                $LapsExpirationDays = $null
                $LapsExpirationTime = $null
            }
        } else {
            $Laps = 'N/A'
        }
        [Array] $Bitlockers = Get-ADObject -Filter 'objectClass -eq "msFVE-RecoveryInformation"' -SearchBase $_.DistinguishedName -Properties 'WhenCreated', 'msFVE-RecoveryPassword' | Sort-Object -Descending
        if ($Bitlockers) {
            $Encrypted = $true
            $EncryptedTime = $Bitlockers[0].WhenCreated
        } else {
            $Encrypted = $false
            $EncryptedTime = $null
        }
        [PSCustomObject] @{
            Name               = $_.Name
            Enabled            = $_.Enabled
            DNSHostName        = $_.DNSHostName
            DistinguishedName  = $_.DistinguishedName
            System             = ConvertTo-OperatingSystem -OperatingSystem $_.OperatingSystem -OperatingSystemVersion $_.OperatingSystemVersion
            LastLogonDate      = $_.LastLogonDate
            Encrypted          = $Encrypted
            EncryptedTime      = $EncryptedTime
            Laps               = $Laps
            LapsExpirationDays = $LapsExpirationDays
            LapsExpirationTime = $LapsExpirationTime
        }
    }
    return $FormattedComputers
}
$FormattedComputers = Get-WinBitlockerAndLapsSummary
$FormattedComputers | Format-Table -AutoSize *

Looks nice right? It's even easier when formatting this with Out-HTMLView command.

Hope you enjoy this one! Of course, I will add this later on to my PSWinDocumentation.AD project.

This post was last modified on July 29, 2019 22:09

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

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…

2 weeks 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…

5 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…

7 months 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…

8 months ago

How to Efficiently Remove Comments from Your PowerShell Script

As part of my daily development, I create lots of code that I subsequently comment…

8 months ago

Unlocking PowerShell Magic: Different Approach to Creating ‘Empty’ PSCustomObjects

Today I saw an article from Christian Ritter, "PowerShell: Creating an "empty" PSCustomObject" on X…

9 months ago