Some users cannot login to new NPS based VPN server

Our environment previous used a Windows 2003 Server running RAS to offer our employees VPN. This server went away for multiple reasons and we built a brand new 2012 R2 server running NPS and RAS.

Since switching over we’ve had a few employees unable to login to the new VPN server. They keep getting “Invalid Username/Password”. Strangely these users had access to a different account that would work from their personal device. This eliminated client side issues as being the culprit.

Checking the Event Logs on the VPN server we found this event:

Log Name:      System
Source:        RemoteAccess
Date:          8/23/2017 10:03:12 AM
Event ID:      20271
Task Category: None
Level:         Warning
Keywords:      Classic
User:          N/A
Computer:      <SERVER FQDN>
Description:
CoId={NA}: The user <DOMAIN>\<USERNAME> connected from xxx.xxx.xxx.xxx but failed an authentication attempt due to the following reason: The remote connection was denied because the user name and password combination you provided is not recognized, or the selected authentication protocol is not permitted on the remote access server.

We had the user login to Webmail to verify their username and password. Everything was fine.

That led us into the text based logs. We found these:

10.x.x.x,<DOMAIN>\<USERNAME>,08/23/2017,09:58:49,RAS,<SERVERNAME>,44,1634,32,<SERVERNAME>,4,10.x.x.x,7,1,5,6,61,5,64,3,65,1,30,104.x.x.x,67,104.x.x.x,31,207.x.x.x,66,207.x.x.x,4108,10.x.x.x,4128,<SERVERNAME>,8132,2,4147,311,4148,MSRASV5.20,4160,MSRASV5.20,4159,MSRAS-0-DESKTOP-DG5CEKG,8158,{552971A4-83C8-4208-A1F7-54834C94498F},4154,Microsoft Routing and Remote Access Service Policy,4155,1,4129,<DOMAIN>\<USERNAME>,25,311 1 104.x.x.x 08/09/2017 14:23:47 1633,4127,4,4130,<DOMAIN FQDN>/User Accounts/<OU>/<OU>/<USERNAME>,4149,ORG VPN Access,8136,1,4136,1,4142,0

10.x.x.x,<DOMAIN>\<USERNAME>,08/23/2017,09:58:49,RAS,<SERVERNAME>,44,1634,25,311 1 104.x.x.x 08/09/2017 14:23:47 1633,8153,0,8111,0,6,2,4130,<DOMAIN FQDN>/User Accounts/<OU>/<OU>/<USERNAME>,4294967206,12,4294967207,2,4294967209,120,4294967210,50,28,3600,7,1,8136,1,4149,ORG VPN Access,4154,Microsoft Routing and Remote Access Service Policy,4155,1,4129,<DOMAIN>\<USERNAME>,4127,4,4120,0x00564955,4136,2,4142,0

10.x.x.x,<DOMAIN>\<USERNAME>,08/23/2017,09:58:49,RAS,<SERVERNAME>,32,<SERVERNAME>,4,10.x.x.x,6,2,7,1,5,6,61,5,64,3,65,1,30,104.x.x.x,67,104.x.x.x,31,207.x.x.x,66,207.x.x.x,25,311 1 104.x.x.x 08/09/2017 14:23:47 1633,44,1634,8,10.x.x.x2,12,1400,28,3600,50,2045,51,1,55,1503507529,45,3,40,1,4108,10.x.x.x,4128,<SERVERNAME>,4147,311,4148,MSRASV5.20,4160,MSRASV5.20,4159,MSRAS-0-DESKTOP-DG5CEKG,8158,{552971A4-83C8-4208-A1F7-54834C94498F},8132,2,4120,0x00564955,4294967206,4,4154,Microsoft Routing and Remote Access Service Policy,4155,1,4136,4,4142,0

10.x.x.x,<DOMAIN>\<USERNAME>,08/23/2017,09:58:51,RAS,<SERVERNAME>,32,<SERVERNAME>,4,10.x.x.x,6,2,7,1,5,6,61,5,64,3,65,1,30,104.x.x.x,67,104.x.x.x,31,207.x.x.x,66,207.x.x.x,25,311 1 104.x.x.x 08/09/2017 14:23:47 1633,44,1634,8,10.x.x.x2,12,1400,28,3600,50,2045,51,1,55,1503507529,45,3,46,0,43,1068,42,1185,48,19,47,26,49,1,40,2,4108,10.x.x.x,4128,<SERVERNAME>,4147,311,4148,MSRASV5.20,4160,MSRASV5.20,4159,MSRAS-0-DESKTOP-DG5CEKG,8158,{552971A4-83C8-4208-A1F7-54834C94498F},8132,2,4120,0x00564955,4294967206,4,4154,Microsoft Routing and Remote Access Service Policy,4155,1,4136,4,4142,0

The tip-off here was “Microsoft Routing and Remote Access Service Policy”. That was not the name of our VPN access policy. In fact that policy is located on a completely separate tab in NPS.

Turns out the issue was a AD account setting:

After some digging I found out that this AD attribute is called ‘msnpallowdialin’ and can have the following values:

Knowing this I wrote a quick PowerShell script to tell me how many accounts we had configured incorrectly:

$usersTrue = Get-ADUser -SearchBase "OU=<OU>,OU=<OU>,DC=DOMAIN,DC=FQDN" -Filter * -Properties msnpallowdialin -Server <DC FQDN> |Where-Object {$_.msnpallowdialin -eq $TRUE}
$usersFalse = Get-ADUser -SearchBase "OU=<OU>,OU=<OU>,DC=DOMAIN,DC=FQDN" -Filter * -Properties msnpallowdialin -Server <DC FQDN> |Where-Object {$_.msnpallowdialin -eq $FALSE}
$usersUnset = Get-ADUser -SearchBase "OU=<OU>,OU=<OU>,DC=DOMAIN,DC=FQDN" -Filter * -Properties msnpallowdialin -Server <DC FQDN> |Where-Object {$_.msnpallowdialin -eq $NULL}

Write-Output "TRUE = $($usersTrue.Count)"
Write-Output "FALSE = $($usersFalse.Count)"
Write-Output "BLANK = $($usersUnset.Count)"

Turns out we had 142 accounts that were incorrect and 1783 accounts that were. All of the accounts that were incorrect have been around a LONG time.

To change this property on all accounts that were set to TRUE or FALSE we used the following script:

$usersTrue = Get-ADUser -SearchBase "OU=<OU>,OU=<OU>,DC=DOMAIN,DC=FQDN" -Filter * -Properties msnpallowdialin -Server <DC FQDN> |Where-Object {$_.msnpallowdialin -eq $TRUE}

foreach ($user in $usersTrue) {

    Set-ADUser $user -clear msnpallowdialin -Server <DC FQDN>

}

$usersFalse = Get-ADUser -SearchBase "OU=<OU>,OU=<OU>,DC=DOMAIN,DC=FQDN" -Filter * -Properties msnpallowdialin -Server <DC FQDN> |Where-Object {$_.msnpallowdialin -eq $FALSE}

foreach ($user in $usersFalse) {

    Set-ADUser $user -clear msnpallowdialin -Server <DC FQDN>

}

$usersTrue = Get-ADUser -SearchBase "OU=<OU>,OU=<OU>,DC=DOMAIN,DC=FQDN" -Filter * -Properties msnpallowdialin -Server <DC FQDN> |Where-Object {$_.msnpallowdialin -eq $TRUE}
$usersFalse = Get-ADUser -SearchBase "OU=<OU>,OU=<OU>,DC=DOMAIN,DC=FQDN" -Filter * -Properties msnpallowdialin -Server <DC FQDN> |Where-Object {$_.msnpallowdialin -eq $FALSE}
$usersUnset = Get-ADUser -SearchBase "OU=<OU>,OU=<OU>,DC=DOMAIN,DC=FQDN" -Filter * -Properties msnpallowdialin -Server <DC FQDN> |Where-Object {$_.msnpallowdialin -eq $NULL}

Write-Output "TRUE = $($usersTrue.Count)"
Write-Output "FALSE = $($usersFalse.Count)"
Write-Output "BLANK = $($usersUnset.Count)"

I didn’t bother making variables of the repeating values. You can just search/replace these scripts. You need to change “OU=<OU>,OU=<OU>,DC=DOMAIN,DC=FQDN” to be the OU of where your users are and “<DC FQDN>” to the FQDN of one of your Domain Controllers.

Powershell script to report on total send/received e-mails in Exchange v2.0

An updated and improved version of my old script from here.

This script has been tested against Exchange 2016 CU4. I do not know if it will work against older versions of Exchange.

The script can be configured to run as a scheduled task and it generates a e-mail report of users who have sent more than ‘x’ e-mails so far today or the previous day.

You can now exclude specific e-mails from the report by placing them in the ‘exceptionList.txt’ file which is created after the scripts first run

We use this script to find compromised accounts that are blasting out spam.

 

# Users who sent more than 'x' e-mails so far today or yesterday
# The output of this report is then e-mailed
#
# This script must be called using the argument '-TimeFrame "yesterday"' or '-TimeFrame "today"'
#
# On first run a file called 'exceptionList.txt' in the same directory as itself if the file doesn't exist. This means the script will need
# read-write access to the location on the first run and read-only access from then on. Once created you can add e-mail addresses into the
# text file (one per line) that you want excluded from the report.
#
# Note: This script has been specifically tested against Exchange 2016 CU4 and may not work against previous versions of Exchange
#
# Requirements: The account that runs this script needs at least the "View-Only Organization Management" and the "Records Management" role in Exchange
#
# Created by: Eric Schewe
# Created on: 2017-08-15
#

# This command must be run with '-timeFrame today' or '-timeFrame yesterday' specified
Param(
    [Parameter(Mandatory=$False)]
    [string]$timeFrame
)

# ------------------------- (START) Customize this section per your deployment -------------------------
# Set the minimum sent e-mails count you care about. Anyone who sends more than this number will appear in the report
$minimumEmails = 500

# Location of script, no trailing slash
$scriptLocation = ""

# E-mail stuff
# Multiple e-mail addresses should be in this format "<[email protected]>, <[email protected]>"
$to = ""
$from = "[email protected]"
$smtpServer = "smtp.mydomain.com"

# Exchange server you want to run this PowerShell against
$exchangePowerShellServer = "exchange01.mydomain.com"
# ------------------------- (END) Customize this section per your deployment -------------------------

if ($timeFrame -match "today") {
    # How we define today
    $startTime = (get-date -Hour 00 -Minute 00 -Second 00)
    $endTime = (get-date -Hour 23 -Minute 59 -Second 59)
    $subject = "Staff who sent over $minimumEmails e-mails for $startTime to now"

}
elseif ($timeFrame -match "yesterday") {

    # How we define yesterday
    $startTime = (get-date -Hour 00 -Minute 00 -Second 00).AddDays(-1)
    $endTime = (get-date -Hour 23 -Minute 59 -Second 59).AddDays(-1)
    $subject = "Staff who sent over $minimumEmails e-mails for $startTime to $endTime"

}
else {

    # Output parameter information if it isn't specified
    Write-Host "Please specify '-timeFrame today' or '-timeFrame yesterday' when invoking this script" -ForeGroundColor "Red"
    Exit

}

# Debugging - This is useful if you're executiong the script under your account and need to test with different credentials
#$UserCredential = Get-Credential
#$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://brockton-03.it.int.viu.ca/PowerShell/ -Authentication Kerberos -Credential $UserCredential

# Production - This assumes you're running this as a scheduled task under a user account with the proper credentials so we don't prompt for credentials
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$exchangePowerShellServer/PowerShell/ -Authentication Kerberos

# Assuming we've gotten here in the script start a Exchange session
Import-PSSession $Session -AllowClobber

# Check if the exception list exists, create it if it doesn't and then read the exception list into the script skipping blank lines and comments
if (!(Test-Path "$($scriptLocation)\exceptionList.txt")) {
    New-Item -path $($scriptLocation) -name exceptionList.txt -type "file" -value "# One entry per line, E-mail addresses are NOT case sensitive" | Out-Null
}
$exceptionList = Get-Content "$($scriptLocation)\exceptionList.txt" | Where-Object { $_ -notmatch "#" -or $_ -notmatch "" }

# Get a list of transport servers in case there is more than one
$transportServers = Get-TransportService

# Initialize the variable so we can append to it. Initial stats array is cast differently because we can't use "Group-Object" on an ArrayList and we can't use .Remove on a Array
$mailStats = @()
[System.Collections.ArrayList]$mailStatsToDelete = @()
[System.Collections.ArrayList]$mailStatsArrayList = @()

# Search the message tracking log on each transport server
foreach ($server in $transportServers) {

    # Search the message tracking log within a time frame on each transport server only looking for the 'SENDEXTERNAL' EventID
    $mailStats += Get-MessageTrackingLog -Server $server.name -Start $startTime -End $endTime -EventID "SENDEXTERNAL" -ResultSize Unlimited

}

# Filter out anyone that didn't send enough e-mails
$mailStats = $mailStats |Group-Object -Property Sender | Where-Object {$_.Group.Recipients.Count -gt $minimumEmails}

# Convert the Array to an ArrayList so we can use the .Remove method and so we can sort things later
foreach ($stat in $mailStats) {

    $line = "" | Select-Object Email,Total
    $line.Email = $stat.Name
    $line.Total = $stat.Group.Recipients.Count
    $mailStatsArrayList += $line

}

# If there are entries in the exception list grab all of the matches and store them in a seperate array
if ($exceptionList.Count -ne 0) {
    # Go through the list of exceptions and find any matches and store them
    foreach ($exception in $exceptionList) {

        foreach ($stat in $mailStatsArrayList) {

            if ($stat.Email -like $exception) {

                $mailStatsToDelete += $stat

            }

        }

    }

    # Remove the matched exceptions from the final results
    foreach ($stat in $mailStatsToDelete) {

        $mailStatsArrayList.Remove($stat)

    }
}

# Check and see if there are any results to report
if ($mailStatsArrayList.Count -ne 0) {

    # Sort and format the output into a table for the e-mail
    $results = $mailStatsArrayList | Sort-Object -Property Total -Descending | Format-Table -Property @{Expression={$_.Total};Label="Count";Width=15; Alignment="left"},@{Expression={$_.Email};Label="Sender"; Width=250; Alignment="left"} |Out-String -width 300

    # Send the e-mail
    $smtp = new-object Net.Mail.SmtpClient($smtpServer)
    $smtp.Send($from, $to, $subject, $results)

}

# Clean-up our session
Remove-PSSession $Session