Powershell Remediation : NPS Failures, No policy matched


This article is the remediation to the previous previous article, I posted here

The original article as an overview identified people that weren’t putting in a required group for NPS to issue a valid EAP token which means the VPN will not connect and that  device will not be able to access and give a limited service to company resources.

Note : When you have a laptop deployment that users initiate themselves it could be very hard to ensure people are in the correct groups when those users can attempt to share laptops and your VPN requires notification to be in those groups, therefore, the purpose of this article is to detect the errors on the MPS server and automatically remediate.

This post addresses the remediation you can automatically perform to fix the problems outlined in the previous post, however, we have two scenarios here that we need to consider:

UPN : If the user is correctly, logging in with their UPN or mail address, depending on what you’ve got set up then in this example, you only need to add them to a single group

samAccountName : If the user is using the samAccountName ot the pre-Windows 2000 logon ID, that would indicate that the normal login method did not work and more importantly, if you have a mixed environment where different devices require a different loginthen the user could easily fall back to the samAccountName

Automate the UPN Login Names

This check your NPS servers find people that match the default policy and if they are using the UPN/email attribute they will be added to the group specified in the script, all variables are in bold.

Script : NPSSearch-UPNAdd.ps1

# Define the array of NPS servers
$NPSServers = @("nps-vpn.bear.local", "nps-vpn2.bear.local")

# Setup logging
$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
$searchLogFile = Join-Path $scriptPath "NPSPolicyUsers_1Hr.log"
$groupAdditionLogFile = Join-Path $scriptPath "GroupAddition.log"

# Clear search log and create new
"" | Set-Content $searchLogFile

$uniqueUsers = @{}
$startTime = (Get-Date).AddHours(-1)
Write-Host "Searching for events from: $($startTime)"

# Search for users across NPS servers
foreach ($server in $NPSServers) {
    Write-Host "Processing server: $server"
        try {
        $events = Get-WinEvent -ComputerName $server -FilterHashtable @{
            LogName = 'Security'
            ID = 6273
            StartTime = $startTime
        } -ErrorAction SilentlyContinue
        if ($events) {
            foreach ($event in $events) {
                $eventMessage = $event.Message
                if ($eventMessage -match "Network Policy Name:\s+Connections to other access servers") {
                    if ($eventMessage -match "Account Name:\s+(.+?)[\r\n]") {
                        $username = $Matches[1].Trim()
                        if (-not $uniqueUsers.ContainsKey($username)) {
                            $uniqueUsers[$username] = $true
                            $logEntry = "{0} - Server: {1}, User: {2}" -f (Get-Date), $server, $username
                            Add-Content -Path $searchLogFile -Value $logEntry
                            Write-Host "Found new user: $username"
                        }
                    }
                }
            }
        } else {
            $message = "No events found on server $server for the past hour"
            Write-Host $message
            Add-Content -Path $searchLogFile -Value "$(Get-Date) - $message"
        }
    }
    catch {
        $errorMessage = "Error processing server $server`: $_"
        Write-Host $errorMessage -ForegroundColor Red
        Add-Content -Path $searchLogFile -Value "$(Get-Date) - $errorMessage"
    }
}
Write-Host "Search complete. Found $($uniqueUsers.Count) unique users"Add-Content -Path $searchLogFile -Value "$(Get-Date) - Total unique users found: $($unique
Users.Count)"

# Process group additions
Write-Host "`nProcessing group additions..."
"" | Set-Content $groupAdditionLogFile
foreach ($username in $uniqueUsers.Keys) {
    try {
        # Get the user's UPN
        $user = Get-ADUser -Filter "SamAccountName -eq '$username'" -Properties UserPrincipalName
        if ($user) {
            $upn = $user.UserPrincipalName
            # Verify it's a valid email format for your domain
            if ($upn -match "[a-zA-Z0-9.-]+@croucher\.cloud") {
                Add-ADGroupMember -Identity "<group_name>" -Members $user
                $successMessage = "$(Get-Date) - Added user: $upn"
                Write-Host $successMessage -ForegroundColor Green
                Add-Content -Path $groupAdditionLogFile -Value $successMessage
            } else {
                $invalidMessage = "$(Get-Date) - Invalid UPN format for user: $username"
                Write-Host $invalidMessage -ForegroundColor Yellow
                Add-Content -Path $groupAdditionLogFile -Value $invalidMessage
            }
        } else {
            $notFoundMessage = "$(Get-Date) - User not found in AD: $username"
            Write-Host $notFoundMessage -ForegroundColor Yellow
            Add-Content -Path $groupAdditionLogFile -Value $notFoundMessage
        }
    }
    catch {
        $errorMessage = "$(Get-Date) - Failed to add user: $username - $($_.Exception.Message)"
        Write-Host $errorMessage -ForegroundColor Red
        Add-Content -Path $groupAdditionLogFile -Value $errorMessage
    }
}
Write-Host "`nProcessing complete."
Write-Host "Search log saved to: $searchLogFile"
Write-Host "Group addition log saved to: $groupAdditionLogFile"

This will then run though the script and process all your NPS servers, add the valid users to the group specified as you can see below:


This will also create a couple of logs (these are shown in blue below) as you review the groupAdditions log to see who have been added to that group, this group is historical as well.

Partially Automate the samAccountName Logins

This will send an e-mail to the intended people as you need to add the user to a local AD group and a Entra group at the same time, this email will outline what needs to be done in order to fix the issue.

Script : samAccountName-Mailer.ps1

# Read the log file
$logContent = Get-Content "NPSPolicyUsers.log"

# Find non-UPN user (without @severntrent.co.uk)
$nonUPNUser = $logContent | Where-Object { $_ -match "User: (\w+)$" } | ForEach-Object { $matches[1] }

$emailParams = @{
    From = "no-reply@croucher.cloud"
    To = "lee@croucher.cloud"
    Subject = "VPN : Configuration Error Detected"
    SmtpServer = "smtpsrv.bear.local"
    Body = @"
The user $nonUPNUser needs to be added to the following groups:

VPN-TwinGate-User  (AD) 
AccessGroup-External-VPN  (AAD)

The user will be unable to authenticate with the VPN at this time which will impact their experience.
"@
}
Send-MailMessage @emailParams

This will then send an email to recipient with the content being the section in italics.

Automate the samAccountName

This does the full scan and then (if you have the access) will then add people to both the local domain group and the remote Entra group and then give a report on what has been done.

Script : NPSSearch-SamAccountAutomted.ps1

# Import required modules
Import-Module ActiveDirectory
Import-Module Microsoft.Graph.Groups
Import-Module Microsoft.Graph.Users

# Define the array of NPS servers
$NPSServers = @("nps-vpn.bear.local", "nps-vpn2.bear.local")

# Script and log file paths
$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
$logFile = Join-Path $scriptPath "NPSPolicyUsers_SamAddition.log"
$auditLogFile = Join-Path $scriptPath "NPSPolicyUsers_Audit.log"

# Initialize log files
"" | Set-Content $logFile
if (-not (Test-Path $auditLogFile)) {
    "" | Set-Content $logFile
}

# Function to convert SAMAccountName to UPN
function Convert-ToUPN {
    param (
        [string]$samAccountName
    )
    try {
        $user = Get-ADUser -Identity $samAccountName -Properties UserPrincipalName
        return $user.UserPrincipalName
    }
    catch {
        throw "Failed to convert $samAccountName to UPN: $_"
    }
}

# Function to add user to AD group
function Add-UserToADGroup {
    param (
        [string]$samAccountName,
        [string]$groupName
    )
    try {
        Add-ADGroupMember -Identity $groupName -Members $samAccountName
        return $true
    }
    catch {
        throw "Failed to add $samAccountName to AD group $groupName: $_"
    }
}

# Function to connect to Microsoft Graph
function Connect-ToGraph {
    try {
        # Connect to Microsoft Graph with required scopes
        Connect-MgGraph -Scopes "Group.ReadWrite.All", "User.ReadWrite.All"
        return $true
    }
    catch {
        throw "Failed to connect to Microsoft Graph: $_"
    }
}

# Function to add user to Entra (Azure AD) group
function Add-UserToEntraGroup {
    param (
        [string]$userPrincipalName,
        [string]$groupName
    )
    try {
        # Get the group ID
        $group = Get-MgGroup -Filter "displayName eq '$groupName'"
        if (-not $group) {
            throw "Group $groupName not found in Entra"
        }

        # Get the user ID
        $user = Get-MgUser -Filter "userPrincipalName eq '$userPrincipalName'"
        if (-not $user) {
            throw "User $userPrincipalName not found in Entra"
        }

        # Add user to group
        New-MgGroupMember -GroupId $group.Id -DirectoryObjectId $user.Id
        return $true
    }
    catch {
        throw "Failed to add $userPrincipalName to Entra group $groupName: $_"
    }
}

$uniqueUsers = @{}
$startTime = (Get-Date).AddHours(-1)

Write-Host "Searching for events from: $($startTime)"

foreach ($server in $NPSServers) {
    Write-Host "Processing server: $server"
    
    try {
        $events = Get-WinEvent -ComputerName $server -FilterHashtable @{
            LogName = 'Security'
            ID = 6273
            StartTime = $startTime
        } -ErrorAction SilentlyContinue

        if ($events) {
            foreach ($event in $events) {
                $eventMessage = $event.Message
                if ($eventMessage -match "Network Policy Name:\s+Connections to other access servers") {
                    if ($eventMessage -match "Account Name:\s+(.+?)[\r\n]") {
                        $samAccountName = $Matches[1].Trim()
                        if (-not $uniqueUsers.ContainsKey($samAccountName)) {
                            $uniqueUsers[$samAccountName] = $true
                            
                            # Log the initial event
                            $logEntry = "{0} - Server: {1}, User: {2}" -f (Get-Date), $server, $samAccountName
                            Add-Content -Path $logFile -Value $logEntry
                            Add-Content -Path $auditLogFile -Value $logEntry

                            # Check if username is non-UPN (doesn't contain @severntrent.co.uk)
                            if ($samAccountName -notmatch "@severntrent\.co\.uk$") {
                                Write-Host "Processing non-UPN user: $samAccountName"
                                
                                try {
                                    # Convert to UPN
                                    $upn = Convert-ToUPN -samAccountName $samAccountName
                                    Add-Content -Path $auditLogFile -Value "$(Get-Date) - Converted $samAccountName to UPN: $upn"

                                    # Add to AD group
                                    Add-UserToADGroup -samAccountName $samAccountName -groupName "VPN-TwinGate-User"
                                    Add-Content -Path $auditLogFile -Value "$(Get-Date) - Added $samAccountName to AD group VPN-TwinGate"

                                    # Prompt for Graph authentication and add to Entra group
                                    Write-Host "Please authenticate to Microsoft Graph to add user to Entra group"
                                    if (Connect-ToGraph) {
                                        Add-UserToEntraGroup -userPrincipalName $upn -groupName "AccessGroup-External-VPN"
                                        Add-Content -Path $auditLogFile -Value "$(Get-Date) - Added $upn to Entra group AccessGroup-External-VPN"
                                    }
                                }
                                catch {
                                    $errorMessage = "Error processing user $samAccountName`: $_"
                                    Write-Host $errorMessage
                                    Add-Content -Path $auditLogFile -Value "$(Get-Date) - $errorMessage"
                                }
                            }
                        }
                    }
                }
            }
        } else {
            $message = "No events found on server $server for the past hour"
            Write-Host $message
            Add-Content -Path $logFile -Value "$(Get-Date) - $message"
            Add-Content -Path $auditLogFile -Value "$(Get-Date) - $message"
        }
    }
    catch {
        $errorMessage = "Error processing server $server`: $_"
        Write-Host $errorMessage
        Add-Content -Path $logFile -Value "$(Get-Date) - $errorMessage"
        Add-Content -Path $auditLogFile -Value "$(Get-Date) - $errorMessage"
    }
}

Write-Host "Processing complete. Log files saved to: $logFile and $auditLogFile"
Write-Host "Total unique users found: $($uniqueUsers.Count)"
Previous Post Next Post

نموذج الاتصال