Integration with Modern Windows LAPS in Netwrix Privilege Secure

Hi everyone,
I’m currently working with Netwrix Privilege Secure for Access Management and setting up integration with LAPS for local credential management.

I noticed that the official documentation still refers to the legacy LAPS download link:

However, in our environment we are using the modern version of Windows LAPS, which is natively integrated into Windows 11 and Windows Server 2022 and managed via Group Policy.

Does anyone know if integration with this modern version of LAPS is officially supported in Netwrix Privilege Secure ?

Specifically, I would like to understand:

  • Does the LAPS connector work with the new attributes introduced by modern Windows LAPS?
  • Is there anything different that needs to be configured compared to the legacy version?
  • Are there any best practices or known limitations?

Thanks in advance for any insights or experiences you can share!

2 Likes

Josephine,

Thanks for reaching out. We currently don’t natively support Windows LAPS but it is on the roadmap. Until then you can leverage the BYOV integration connector with custom pwsh code to access Windows LAPS accounts/passwords. I have scripts to do this in Azure but it is specific to Azure only or azure accounts with password writeback enabled. Also, outside of Windows LAPS you can utilize resource groups within NPS-AM that can automatically manage local administrator accounts. This would be used instead of LAPS. Are you just looking to have access to these accounts/passwords in NPS-AM? If so a BYOV connector and pwsh can checkout/checkin these accounts and accomplish this. Below is my checkout script for working with LAPS in azure.



param(
    [Parameter(Mandatory = $true)]
    $Options,
    [Parameter(Mandatory = $true)]
    $Credential
)
# Extract the Azure vault information
$azureTenantId = $Options.Connector.Options.APIUrl
$azureAppId = $Options.Connector.Options.AppId
$CertificateThumbprint = $Options.Connector.Options.ClientCertificate

$AZApp = $Options.Connector.Name

if ($null -ne $CertificateThumbprint) {
    Add-SbPAMActionLog -Type Info -Message "Using certificate thumbprint: '$CertificateThumbprint'"
    $azureCert = Get-ChildItem Cert:\LocalMachine\ -Recurse | Where-Object { $_.Thumbprint -eq $CertificateThumbprint } | Select-Object -First 1 
    if ($null -eq $azureCert) {
        throw "Unable to find certificate '$CertificateThumbprint'"
    }
}
$TargetHost = $Options.TargetHost
if ($null -eq $targetHost -or [string]::IsNullOrEmpty($targetHost.DnsHostName)) {
    Throw "Unable to find host."
}

Add-SbPAMActionLog -Type Info -Message "Checking out LAPS password for $($credential.username) from $AZApp" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
Add-SbPAMActionLog -Type Info -Message "Connecting to AZ with AppId '$azureAppID' TenantId '$azureTenantId'"
$graphConn = $null
    
try {
    $graphConn = Connect-MgGraph -ClientId $azureAppId -TenantId $azureTenantId -Certificate $azureCert -NoWelcome -ErrorAction Stop
    # Connecting to MS Graph
    Write-Host "Connected to MS Graph using application '$azureAppID' in tenant '$azureTenantId'"
    
    if ($null -ne $Options.ActivitySession) {
        $Username = $Options.ActivitySession.LoginAccountName
        $Hostname = $TargetHost.DnsHostName
    }
    
    #OR
    #$hostname = $Whatever I need to pull the hostname from the AppOnlyAuthGraph secret vault.  
    # Get the Azure LAPS Password

    Write-Host "Getting Azure LAPS password for $hostname"
    # Get the LAPS password for the specified device
    $passwordInfo = Get-LapsAADPassword -DeviceIds $hostname -IncludePasswords -AsPlainText -ErrorAction Stop

    # Extract the password
    $secret = $passwordInfo.Password

    if($null -eq $secret) {
        throw "Password for $hostname not found"
    }

    # Set the credential password
    $Credential.Username = $Username
    $credential.password = $secret
    ##Need alternative if I can't map to a credential???

    Add-SbPAMActionLog -Type Info -Message "Checked out Azure LAPS password for $($Hostname) using $AZApp" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null

    # Return the credential so the wrapper can save it
    return $credential
}
catch {
    Add-SbPAMActionLog -Type Error -Message "Error checking out LAPS password for $($Hostname) using $AZApp : $($_)" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null    
    return $null
}
finally {
    if ($null -ne $graphConn) {
        Add-SbPAMActionLog -Type Info -Message "Disconnect Microsoft Graph"
        Disconnect-MgGraph | Out-Null
    }
}

4 Likes

Thank you for sharing this Adam, in order to use this all the ActionServices would need to have that certificate installed, right? And is that a certificate that you created and uploaded? Or is that a certificate that is generated in Azure?

Just curious what the setup for this BYOV script would be.

Thanks!

Yes, like anything utilizing app-only access and the MS Graph powershell module, a valid certificate is needed for client – server authentication between NPS-AM Action Service servers and EntraID. The Base64 Public Key file is used by the Azure registered app to authenticate. Also, the Azure App Registration requires permission to use Microsoft.Graph and read device permissions and read device local credential passwords.

Additionally, each NPS-AM Action Service server (meaning each server where NPS-AM is installed and any individual Action Service servers) need:

  • the cert used for auth to be valid (complete certificate chain to the Root Store) and installed
  • Powershell 7 and the Microsoft.Graph Module installed

Thanks so much for the detailed responses and for sharing the script!
Just to clarify our environment: we are not using Azure , but rather a fully on-premises Active Directory setup on Domain Controllers. The modern version of Windows LAPS is already deployed and working in our infrastructure, managed via Group Policy.
Our goal is to integrate this modern Windows LAPS with Netwrix Privilege Secure (NPS-AM), so we can retrieve the local admin credentials managed by LAPS directly within NPS-AM.
If anyone has already implemented this integration or has tips I’d really appreciate any advice or best practices!

Thank you all!

1 Like

Hey Again Josephine. I had a feeling you needed to pull this directly from AD but wanted to get you something so you could get a feel for how the integrations work. I put this together for Windows LAPS in AD too. It works in my lab but that’s no guarantte so please test thoroughly if you do want to use this in a production environment. Hopefully this is closer to what you are looking for.

First, go to Integration Connectors and create a BYOV connector using the same setting here:

For the checkout script copy and past the following:

param(
    [Parameter(Mandatory = $true)]
    $Options,
    [Parameter(Mandatory = $true)]
    $Credential
)

# Extract the connector information
$connectorName = $Options.Connector.Name

# Get target host information
$TargetHost = $Options.TargetHost
if ($null -eq $TargetHost -or [string]::IsNullOrEmpty($TargetHost.DnsHostName)) {
    Throw "Unable to find host."
}
$Hostname = $TargetHost.DnsHostName

# Get the ServiceAccount credential using CredentialId
$ServiceAccountCredentialId = $Options.TargetHost.CredentialId
if ($null -eq $ServiceAccountCredentialId -or [string]::IsNullOrEmpty($ServiceAccountCredentialId)) {
    Add-SbPAMActionLog -Type Error -Message "ServiceAccount CredentialId not found in Options.TargetHost.CredentialId" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
    return $null
}

# Retrieve the ServiceAccount credential using the GUID
$ServiceAccountCredential = Get-SbPAMCredential -Id $ServiceAccountCredentialId -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken
if ($null -eq $ServiceAccountCredential) {
    Add-SbPAMActionLog -Type Error -Message "Failed to retrieve ServiceAccount credential with ID: $ServiceAccountCredentialId" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
    return $null
}

# Convert to PSCredential using manual method
$username = "$($ServiceAccountCredential.Domain)\$($ServiceAccountCredential.Username)"
$securePassword = ConvertTo-SecureString -String $ServiceAccountCredential.Password -AsPlainText -Force
$PSCredential = New-Object System.Management.Automation.PSCredential($username, $securePassword)

Add-SbPAMActionLog -Type Info -Message "Checking out Windows LAPS password for $Hostname from $connectorName using service account: $($PSCredential.UserName)" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null

try {
    Add-SbPAMActionLog -Type Info -Message "Running LAPS query on domain controller " -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
    
    # Dynamically discover domain controllers
    try {
        Add-SbPAMActionLog -Type Info -Message "Discovering domain controllers for $($ServiceAccountCredential.Domain)" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
        
        # Try to get domain controllers using DNS SRV queries
        $domainControllers = @()
        
        try {
            $dnsResult = Resolve-DnsName -Name "_ldap._tcp.$($ServiceAccountCredential.Domain)" -Type SRV -ErrorAction Stop
            foreach ($record in $dnsResult) {
                if ($record.NameTarget) {
                    $domainControllers += $record.NameTarget
                }
            }
            Add-SbPAMActionLog -Type Info -Message "Found domain controllers via DNS: $($domainControllers -join ', ')" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
        } catch {
            Add-SbPAMActionLog -Type Error -Message "DNS SRV lookup failed: $($_.Exception.Message)" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
            throw "Unable to discover domain controllers for $($ServiceAccountCredential.Domain). DNS SRV lookup failed."
        }
        
        if ($domainControllers.Count -eq 0) {
            throw "No domain controllers found for $($ServiceAccountCredential.Domain)"
        }
    }
    catch {
        Add-SbPAMActionLog -Type Error -Message "Domain controller discovery failed: $($_.Exception.Message)" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
        return $null
    }
    
    $lapsInfo = $null
    $dcUsed = $null
    
    foreach ($dc in $domainControllers) {
        try {
            Add-SbPAMActionLog -Type Info -Message "Trying LAPS query on domain controller: $dc" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
            
            $result = Invoke-Command -ComputerName $dc -Credential $PSCredential -ScriptBlock {
                param($TargetComputer)
                try {
                    Import-Module LAPS -ErrorAction SilentlyContinue
                    # Try FQDN first
                    $lapsResult = Get-LapsADPassword -Identity $TargetComputer -AsPlainText -ErrorAction Stop
                    return $lapsResult
                }
                catch {
                    # Try with computer name suffix
                    $computerName = $TargetComputer + "$"
                    $lapsResult = Get-LapsADPassword -Identity $computerName -AsPlainText -ErrorAction Stop
                    return $lapsResult
                }
            } -ArgumentList $Hostname
            
            if ($null -ne $result -and $null -ne $result.Password) {
                $lapsInfo = $result
                $dcUsed = $dc
                Add-SbPAMActionLog -Type Info -Message "LAPS password successfully retrieved from domain controller: $dc" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
                break
            }
        }
        catch {
            Add-SbPAMActionLog -Type Info -Message "Domain controller $dc failed: $($_.Exception.Message)" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
        }
    }
    
    # Extract the password
    if ($null -ne $lapsInfo -and $null -ne $lapsInfo.Password) {
        $secret = $lapsInfo.Password
        
        # Set the credential with administrator username and LAPS password
        $userName = if ($null -ne $Options.ActivitySession) { $Options.ActivitySession.LoginAccountName } else { $Credentials.Username }
    				if ($userName.Contains("\")) {
       						 $domain, $userName = $userName -Split "\\"
   		 }

        $Credential.Domain = $domain
        $Credential.Username = $UserName
        $Credential.Password = $secret
        
        # Log the expiration time if available
        if ($null -ne $lapsInfo.ExpirationTimestamp) {
            Add-SbPAMActionLog -Type Info -Message "LAPS password expires: $($lapsInfo.ExpirationTimestamp)" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
        }
        
        Add-SbPAMActionLog -Type Info -Message "Successfully checked out Windows LAPS password for $Hostname using domain controller $dcUsed" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
        
        # Return the credential so PAM can save it
        return $Credential
    } else {
        Add-SbPAMActionLog -Type Error -Message "All domain controllers failed to retrieve LAPS password for $Hostname" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null
        return $null
    }
}
catch {
    Add-SbPAMActionLog -Type Error -Message "Error checking out Windows LAPS password for $Hostname - $($_.Exception.Message)" -RestApiUri $Options.RestApiUri -RestApiToken $Options.RestApiToken | Out-Null    
    return $null
}

Leave the checkin field blank.

Then create an Activity that uses the integration connector we just created and the settings here (If you have changed your local administrator account names replace the value to match in the Login Account Template field):

Finally, tie everything together via an Access Policy that is using a connection profile that allows for password viewing (alternatively you can require this only be used with the RDP files through the proxy service and not allow for the password to be viewed)

I hope this helps!

3 Likes

Thanks so much! I really appreciate you putting this together!

I’ll definitely run some thorough tests in my environment and let you know how it goes. Thanks again for sharing your work!

Hi again!
I followed the procedure as you described:

  1. Created the BYOV connector in the Integration Connectors section using the provided settings.

  2. Pasted the checkout script exactly as instructed and left the check-in field blank.

  3. Created an Activity using the connector and applied the settings, including adjusting the Login Account Template to match our local administrator account name.

  4. Linked everything via an Access Policy

However, when I tested the integration, it resulted in an error. I’ll include a screenshot and the relevant log entry to help troubleshoot.


statusString,status,logMessage,lineNumber,timestamp,version,id
"Warn",2,"DisableUser script finished with errors, [REDACTED]: 8ae5f530-b9aa-436f-b8f3-ee167b6c7ffb.",18,"2025-07-02T09:48:00.690472Z","4.2.1632.0","18"
"Error",3,"Unable to find user to Disable.",17,"2025-07-02T09:48:00.662966Z","4.2.1632.0","17"
"Error",3,"Failed to checkout user from [REDACTED]: Failed to checkout user from [REDACTED].",16,"2025-07-02T09:47:59.779386Z",,"16"
"Error",3,"[[REDACTED].[REDACTED]] Connecting to remote server [REDACTED].[REDACTED] failed with the following error message : The WinRM client cannot process the request...",15,"2025-07-02T09:47:59.759057Z",,"15"
...
"Info",1,"Trying LAPS query on domain controller: [REDACTED].[REDACTED]",9,"2025-07-02T09:47:59.620116Z","4.2.1632.0","9"
"Info",1,"Checking out Windows LAPS password for [REDACTED].[REDACTED] from LAPS using service account: [REDACTED]\\[REDACTED]",3,"2025-07-02T09:47:59.311206Z","4.2.1632.0","3"

Let me know if you’d like me to share any specific configuration details or if you have suggestions on what to check next.

Thanks so much in advance!!

1 Like

It looks to be a permissions issue. Do your windows resources use the same service account as Active Directory? You need to make sure that account has the following permissions:

Domain Controller Access Permissions:

  • Remote PowerShell Access: The service account must be a member of a group that has “Log on as a service” and “Access this computer from the network” rights on the DC
  • WinRM/PowerShell Remoting: Typically requires membership in the Remote Management Users group or Administrators group on the target DC

LAPS-Specific Permissions:

  • Read permissions on the LAPS password attribute in Active Directory
  • The account needs to be granted “All extended rights” or specifically ms-LAPS-Password attribute permission on the computer objects whose LAPS passwords you want to retrieve. Typically done via a group.

You can use this to set permissions for the account

Set-LapsADReadPasswordPermission -Identity "OU=Computers,DC=domain,DC=com" -AllowedPrincipals "Domain\serviceaccountname"

The Windows LAPS PowerShell module must be installed on the system you’re running the command from. The script should handle this.

You can see what account is being used on the resource on its resource page in NPS-AM

We have already verified and applied all the required permissions:

  • The service account is a Domain Admin, and therefore already has:
    • “Log on as a service”
    • “Access this computer from the network”
    • Remote PowerShell Access via membership in the Administrators group
  • Regarding Windows LAPS, the account has:
    • Read permissions on the ms-LAPS-Password attribute
    • All extended rights on the relevant computer objects

Despite all of this being in place, the error remains unchanged.

Would it be possible to contact support regarding this issue (even if this feature is not yet fully implemented)?

Thank you so much for your responses and help!

1 Like

Hi Josephine,
Sorry, we couldn’t get this to work! With how deep into the weeds it was getting, I had high hopes!

As Adam noted above, this feature is on the roadmap, though!. If you want to create an Idea in Privilege Secure Ideas, the product team will give updates as it gets rolled into NPS :slight_smile:

1 Like