Here's our script that we use to pull all registered MFA methods for our users. You'll want to run this in an Adaxes Scheduled Task. As far as I know, you can't retrieve the serial number - the only options to do so are to physically look at the key or use the Yubikey programming tool.
We have our script set to run a few times a day. We then have a simple report that returns the stored data (see Step 1 below).
Somre prerequisites:
- A custom attribue in Adaxes to store the data in. We use
adm-CustomAttributeTextMultiValue2
- you'll need to modify line 98 in the script accordingly.
- An Entra app registration that uses a certificate for authentication. The app must have at a minimum
User.Read
delegated permissions (more permissions may be required, but I think this should suffice).
- The certificate for the app stored in
Local Computer\Personal\Certificates
on your Adaxes server.
- You'll need to know which model(s) of Yubikey you have, and add the
AAGUID
for each model (found here). You'll then need to modify the switch
statement (around line 63 in the script) to include each AAGUID
you have. We only use one model - the YubiKey 5 NFC - so the script looks for the AAGUID
of 2fc0579f-8113-47ea-b116-bb5a8db9202a
.
- You'll need to edit the variables on lines 8, 9, and 10.
- Finally, you'll need the
Microsoft.Graph.Users
Powershell module installed for all users on your Adaxes server.
<#
.SYNOPSIS
Uses the Microsoft Graph API to query user's registered MFA methods and updates the adm-CustomAttributeTextMultiValue2 attribute.
#>
$certThumbprint = "your cert thumbprint"
$tenantId = "your Entra tenant ID"
$clientId = "your Entra app ID"
# check if we have the cert for graph authentication
try {
if (-not(Get-ChildItem -Path "Cert:\$certThumbprint" -Recurse)) {
throw [Microsoft.PowerShell.Commands.CertificateNotFoundException]::New("Could not find certificate with thumbprint $certThumbprint")
}
}
catch {
$Context.LogException($_.Exception)
break
}
try {
# set up the graph auth parameters
$graphAuthParams = @{
tenantId = $tenantId
clientId = $clientId$
CertificateThumbprint = $certThumbprint
}
Connect-MgGraph @graphAuthParams -NoWelcome -ErrorAction Stop
$users = Get-MgUser -All
}
catch {
$Context.LogMessage("Failed to connect to Microsoft Graph", "Error")
$Context.LogException($_.Exception)
break
}
# store authentication methods in a hashtable
$userMethods = @{}
foreach ($user in $users) {
try {
$admUser = Get-AdmUser -Identity $user.userprincipalname -ErrorAction SilentlyContinue
if (-not($admUser)) {
# user exists in graph but not adaxes
$Context.LogMessage("User $($user.userprincipalname) not found in Adaxes", "Information")
continue
}
$admUserContext = $Context.BindToObjectByDN($admUser.DistinguishedName)
# all of our "real" users will have a user mailbox (as opposed to shared, room, etc.)
# see https://www.adaxes.com/sdk/ADM_EXCHANGE_MAILBOXTYPE_ENUM/
if ($admUserContext.MailboxType -eq "ADM_EXCHANGE_MAILBOXTYPE_USER") {
#$Context.LogMessage("Getting MFA methods for $($admUserContext.Name)", "Information")
$methods = Get-MgUserAuthenticationMethod -UserId $user.id
$registeredMethods = @()
foreach ($method in $methods) {
switch ($method.AdditionalProperties["@odata.type"]) {
"#microsoft.graph.fido2AuthenticationMethod" {
switch ($method.AdditionalProperties['aaGuid']) {
'90a3ccdf-635c-4729-a248-9b709135078f' { $registeredMethods += "Passkey" }
'2fc0579f-8113-47ea-b116-bb5a8db9202a' { $registeredMethods += $method.AdditionalProperties['model'] }
}
}
"#microsoft.graph.microsoftAuthenticatorAuthenticationMethod" {
$registeredMethods += "Authenticator App"
}
"#microsoft.graph.windowsHelloForBusinessAuthenticationMethod" {
$registeredMethods += "WHfB"
}
# users shouldn't have a phone method registered - we can use this in reports later
"#microsoft.graph.phoneAuthenticationMethod" {
$registeredMethods += "Phone"
}
}
}
$userMethods.Add($user.userprincipalname, ($registeredMethods | Select-Object -Unique))
}
}
catch {
$Context.LogException($_.Exception)
}
}
foreach ($method in $userMethods.GetEnumerator()) {
# only process users who have MFA configured
if ($method.Value) {
#$Context.LogMessage("Updating MFA method list for $($method.Key)", "Information")
# custom attributes can only be set when using the -adaxesservice parameter
Set-AdmUser -Identity $method.Key -Replace @{'adm-customattributetextmultivalue2' = $method.Value } -AdaxesService localhost -ErrorAction SilentlyContinue
}
}
Disconnect-MgGraph -ErrorAction SilentlyContinue