Powershell : NPS Error Log Extraction to html

If you have NPS servers in your organisation that are good at handling 802.1x (PEAP) requests or Windows based authentication or certificate based authentication (EAP) then you need a way of keeping your eye on the failures to ensure smooth operating then you need to review the Security log for Event ID 6273 (which is a failure request)

This means would it not be nice to query these events and then have them presented in a easy to read format like a website, well that is the purpose of this article.

When you get these events we need to parse those events, first you need to know how those events will be presented, this Powershell will show you how this data will be presented to the script:

$scriptBlock = {
    $event = Get-WinEvent -FilterHashtable @{
        LogName = 'Security'
        ID = 6273
        StartTime = (Get-Date).AddHours(-2)
    } -ErrorAction Stop | Select-Object -First 1
    Write-Host "Properties Count: $($event.Properties.Count)"   
    # Inspect each property
    for ($i = 0; $i -lt $event.Properties.Count; $i++) {
        Write-Host "Property[$i]: $($event.Properties[$i].Value)"
    }
}
Invoke-Command -ComputerName "nps-a.bear.local" -ScriptBlock $scriptBlock 

That should then look something like this, in this example I have 26 properties:

Properties Count: 26
Property[0]: S-1-5-21-142122122-1548130249-1115540648-15957
Property[1]: nps.user
Property[2]: BEAR
Property[3]: BEAR/nps.user
Property[4]: S-1-0-0
Property[5]: -
Property[6]: -
Property[7]: F8-2B-28-7F-1A-84:BEAR
Property[8]: DA-51-79-44-26-62
Property[9]: 10.80.441.99
Property[10]: -
Property[11]: F8-9E-28-7F-3D-84:vap0
Property[12]: Wireless - IEEE 802.11
Property[13]: 3
Property[14]: Unifi-Controller1
Property[15]: 10.80.441.22
Property[16]: Secure Wireless Connections
Property[17]: Wifi Traffic
Property[18]: Windows
Property[19]: nps-a.bear.local
Property[20]: PEAP
Property[21]: -
Property[22]: 45394531453734433541323942424346
Property[23]: 23
Property[24]: An error occurred during the Network Policy Server use of the Extensible Authentication Protocol (EAP). Check EAP log files for EAP errors.
Property[25]: Accounting information was written to the local log file.

This property ID will be used in the script later on, but we need to get which field maps to which column in the HTML report before we can then script a automated report.

Produce the html report

Note : This report will only look back for the last 2 hours, if you wish to increase this value you can amend this value in the script below:

StartTime = (Get-Date).AddHours(-2)

If you wish to complete this task for 2 days then you can replace it with this:

StartTime = (Get-Date).AddDays(-2)

This will query all the defined NPS servers for valid events and then produce a html report, as who does not love a good graphical report, this will look like this:


Script : NPSFailureQuery.ps1

# Script Variables

$NPSServers = @(
    "nps-a.bear.local",
    "nps.-b.bear.local"
)
$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
$ReportName = "nps-report.html"
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
Write-Host "Script started at: $(Get-Date)" -ForegroundColor Green
Write-Host "Current user: $($currentUser.Name)" -ForegroundColor Green
Write-Host "Querying servers: $($NPSServers -join ', ')" -ForegroundColor Green
Write-Host "Output will be saved to: $scriptPath\$ReportName" -ForegroundColor Green
Write-Host "`nGathering data..." -ForegroundColor Yellow
$style = @"
<style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    html { font-size: 62.5%; }
    body {
        font-family: Arial, sans-serif;
        font-size: 1.4rem;
        line-height: 1.6;
        color: #333333;
        background-color: #f5f5f5;
        padding: 2rem;
    }
    .container {
        max-width: 98%;
        margin: 0 auto;
        background: #ffffff;
        padding: 2rem;
        border-radius: 0.4rem;
        box-shadow: 0 0.1rem 0.3rem rgba(0, 0, 0, 0.1);
    }
    .header { margin-bottom: 2rem; padding-bottom: 1rem; border-bottom: 0.1rem solid #eee; }
    h1 { font-size: 2.4rem; color: #2c3e50; margin-bottom: 1rem; }
    .timestamp { color: #7f8c8d; font-size: 1.2rem; }   
    /* Health Cards */
    .health-cards {
        display: flex;
        gap: 2rem;
        margin-bottom: 2rem;
    }
    .card {
        flex: 1;
        padding: 1.5rem;
        border-radius: 0.4rem;
        background: #ffffff;
        box-shadow: 0 0.2rem 0.4rem rgba(0, 0, 0, 0.1);
    }
    .card-title { font-size: 1.4rem; color: #7f8c8d; margin-bottom: 0.5rem; }
    .card-value { font-size: 2.4rem; font-weight: bold; color: #2c3e50; }
    .card-green { border-left: 4px solid #2ecc71; }
    .card-yellow { border-left: 4px solid #f1c40f; }
    .card-red { border-left: 4px solid #e74c3c; }
    /* Table Styles */
    .table-container { width: 100%; }
    table { 
        width: 100%;
        table-layout: fixed;
        border-collapse: collapse;
        font-size: 1.2rem;
    }
    th, td {
        padding: 1rem;
        text-align: left;
        border-bottom: 0.1rem solid #eee;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    th {
        background-color: #f8f9fa;
        color: #2c3e50;
        font-weight: bold;
    }
    td:hover {
        white-space: normal;
        overflow: visible;
    }
    tr:hover { background-color: #f9f9f9; }
    .failure-code { color: #e74c3c; }
    /* Column Widths */
    th:nth-child(1), td:nth-child(1) { width: 12%; } /* Time */
    th:nth-child(2), td:nth-child(2) { width: 10%; } /* Server */
    th:nth-child(3), td:nth-child(3) { width: 15%; } /* Device/User */
    th:nth-child(4), td:nth-child(4) { width: 8%; }  /* Failure Code */
    th:nth-child(5), td:nth-child(5) { width: 25%; } /* Failure Reason */
    th:nth-child(6), td:nth-child(6) { width: 12%; } /* CRP Policy */
    th:nth-child(7), td:nth-child(7) { width: 10%; } /* Network Policy */
    th:nth-child(8), td:nth-child(8) { width: 8%; }  /* Auth Type */
</style>
"@
$results = @()
$totalEventsProcessed = 0
foreach ($server in $NPSServers) {
    try {
        Write-Host "`nConnecting to server: $server" -ForegroundColor Cyan
$scriptBlock = {
    param($ServerName)
    Write-Output "Querying events on $ServerName..."   
    Get-WinEvent -FilterHashtable @{
        LogName = 'Security'
        ID = 6273
        StartTime = (Get-Date).AddHours(-2)
    } -ErrorAction Stop | ForEach-Object {
        $event = $_       
        [PSCustomObject]@{
            TimeCreated = $event.TimeCreated
            DeviceUser = $event.Properties[3].Value  # Full Account Name
            FailureCode = $event.Properties[23].Value  # Error Code
            FailureReason = "$($event.Properties[23].Value) - $($event.Properties[24].Value)"  # Error Code + Reason
            CRPPolicy = $event.Properties[16].Value  # Connection Request Policy
            NetworkPolicy = $event.Properties[17].Value  # Network Policy
            AuthType = $event.Properties[20].Value  # Auth Type (PEAP)
        }
    }
}
        $events = Invoke-Command -ComputerName $server -ScriptBlock $scriptBlock
        $totalEventsProcessed += $events.Count
        Write-Host "Successfully retrieved $($events.Count) events from $server" -ForegroundColor Green
                foreach ($event in $events) {
            $data = @{
                Server = $server
                TimeCreated = $event.TimeCreated
                DeviceUser = $event.DeviceUser
                FailureCode = $event.FailureCode
                FailureReason = $event.FailureReason
                CRPPolicy = $event.CRPPolicy
                NetworkPolicy = $event.NetworkPolicy
                AuthType = $event.AuthType
            }           
            $results += [PSCustomObject]$data
        }
    }
    catch {
        Write-Host "Error querying server $server" -ForegroundColor Red
        Write-Host $_.Exception.Message -ForegroundColor Red
    }
}
Write-Host "`nGenerating report..." -ForegroundColor Yellow
$totalFailures = $results.Count
$last24Hours = $results | Where-Object { $_.TimeCreated -gt (Get-Date).AddHours(-24) }
$last24HoursCount = $last24Hours.Count
$uniqueUsers = ($results | Select-Object -ExpandProperty DeviceUser -Unique).Count
Write-Host "Total failures processed: $totalFailures" -ForegroundColor Cyan
Write-Host "Failures in last 24 hours: $last24HoursCount" -ForegroundColor Cyan
Write-Host "Unique users affected: $uniqueUsers" -ForegroundColor Cyan
$tableRows = $results | Sort-Object TimeCreated -Descending | ForEach-Object {
    @"
    <tr>
        <td>$($_.TimeCreated)</td>
        <td>$($_.Server)</td>
        <td>$($_.DeviceUser)</td>
        <td class="failure-code">$($_.FailureCode)</td>
        <td>$($_.FailureReason)</td>
        <td>$($_.CRPPolicy)</td>
        <td>$($_.NetworkPolicy)</td>
        <td>$($_.AuthType)</td>
    </tr>
"@
}
$html = @"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>NPS Authentication Failures Report</title>
    $style
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>NPS Authentication Failures Report</h1>
            <div class="timestamp">Report generated: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")</div>
            <div class="timestamp">Generated by: $($currentUser.Name)</div>
        </div>
             <div class="health-cards">
            <div class="card card-red">
                <div class="card-title">Total Failures</div>
                <div class="card-value">$totalFailures</div>
            </div>
            <div class="card card-yellow">
                <div class="card-title">Last 2 Hours</div>
                <div class="card-value">$totalFailures</div>
            </div>
            <div class="card card-green">
                <div class="card-title">Unique Users</div>
                <div class="card-value">$uniqueUsers</div>
            </div>
        </div>
        <div class="table-container">
            <table>
                <thead>
                    <tr>
                        <th>Time</th>
                        <th>Server</th>
                        <th>Device/User</th>
                        <th>Failure Code</th>
                        <th>Failure Reason</th>
                        <th>CRP Policy</th>
                        <th>Network Policy</th>
                        <th>Auth Type</th>
                    </tr>
                </thead>
                <tbody>
                    $tableRows
                </tbody>
            </table>
        </div>
    </div>
</body>
</html>
"@

$ReportPath = Join-Path $scriptPath $ReportName
$html | Out-File $ReportPath -Encoding UTF8
Write-Host "`nReport generated successfully at: $ReportPath" -ForegroundColor Green
Write-Host "Script completed at: $(Get-Date)" -ForegroundColor Green

When run this will produce the report called nps-report.html which you can see highlighted below:


However that drew my attention to these alerts, here we can see alerts saying that "user must change their password" this is an action point for the user and that user is trying to join corporate services with an expired password:


This would mean that if these events are being detected the user is not being authorised on NPS but being declined, so lets get a script to produce a report for all the users in this scenario but we only want one user on the website, so the data needs to be normalised so the same user does not appear over and over again.

User Password change report

This is how this report will look when run, where it will identify all the users that need to change their passwords to use the NPS protected resources:



Note : This script will look for the last six hours, amend as required with the variable in bold  as below in the script.

Script : NPSPasswordChange.ps1

# Script Variables
$NPSServers = @(
    "nps-a.bear.local",
    "nps.-b.bear.local"
)
$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
$ReportName = "password-change-report.html"
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()

Write-Host "Script started at: $(Get-Date)" -ForegroundColor Green
Write-Host "Current user: $($currentUser.Name)" -ForegroundColor Green
Write-Host "Querying servers: $($NPSServers -join ', ')" -ForegroundColor Green
Write-Host "Output will be saved to: $scriptPath\$ReportName" -ForegroundColor Green
Write-Host "`nGathering data..." -ForegroundColor Yellow

$style = @"
<style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    html { font-size: 62.5%; }
    body {
        font-family: Arial, sans-serif;
        font-size: 1.4rem;
        line-height: 1.6;
        color: #333333;
        background-color: #f5f5f5;
        padding: 2rem;
    }
    .container {
        max-width: 98%;
        margin: 0 auto;
        background: #ffffff;
        padding: 2rem;
        border-radius: 0.4rem;
        box-shadow: 0 0.1rem 0.3rem rgba(0, 0, 0, 0.1);
    }
    .header { margin-bottom: 2rem; padding-bottom: 1rem; border-bottom: 0.1rem solid #eee; }
    h1 { font-size: 2.4rem; color: #2c3e50; margin-bottom: 1rem; }
    .timestamp { color: #7f8c8d; font-size: 1.2rem; }
    
    /* Health Cards */
    .health-cards {
        display: flex;
        gap: 2rem;
        margin-bottom: 2rem;
    }
    .card {
        flex: 1;
        padding: 1.5rem;
        border-radius: 0.4rem;
        background: #ffffff;
        box-shadow: 0 0.2rem 0.4rem rgba(0, 0, 0, 0.1);
    }
    .card-title { font-size: 1.4rem; color: #7f8c8d; margin-bottom: 0.5rem; }
    .card-value { font-size: 2.4rem; font-weight: bold; color: #2c3e50; }
    .card-yellow { border-left: 4px solid #f1c40f; }

    /* Table Styles */
    .table-container { width: 100%; }
    table { 
        width: 100%;
        table-layout: fixed;
        border-collapse: collapse;
        font-size: 1.2rem;
    }
    th, td {
        padding: 1rem;
        text-align: left;
        border-bottom: 0.1rem solid #eee;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    th {
        background-color: #f8f9fa;
        color: #2c3e50;
        font-weight: bold;
    }
    td:hover {
        white-space: normal;
        overflow: visible;
    }
    tr:hover { background-color: #f9f9f9; }
    .alert { color: #e67e22; }
</style>
"@

$results = @()
$totalEventsProcessed = 0

foreach ($server in $NPSServers) {
    try {
        Write-Host "`nConnecting to server: $server" -ForegroundColor Cyan
$scriptBlock = {
    param($ServerName)
    Write-Output "Querying events on $ServerName..."
    
    Get-WinEvent -FilterHashtable @{
        LogName = 'Security'
        ID = 6273
        StartTime = (Get-Date).AddHours(-6)
    } -ErrorAction Stop | ForEach-Object {
        $event = $_
        
        # Only process events with reason code 33
        if ($event.Properties[23].Value -eq "33") {
            [PSCustomObject]@{
                TimeCreated = $event.TimeCreated
                DeviceUser = $event.Properties[3].Value  # Full Account Name
                AuthType = $event.Properties[20].Value  # Auth Type
                CRPPolicy = $event.Properties[16].Value  # Connection Request Policy
                LastAttempt = $event.TimeCreated
                ReasonCode = $event.Properties[23].Value  # Reason Code
                Reason = $event.Properties[24].Value  # Reason Text
            }
        }
    }
}

        $events = Invoke-Command -ComputerName $server -ScriptBlock $scriptBlock
        $totalEventsProcessed += $events.Count
        Write-Host "Successfully retrieved $($events.Count) password change events from $server" -ForegroundColor Green
        
        $results += $events
    }
    catch {
        Write-Host "Error querying server $server" -ForegroundColor Red
        Write-Host $_.Exception.Message -ForegroundColor Red
    }
}

Write-Host "`nGenerating report..." -ForegroundColor Yellow

# Get unique users (take the latest attempt for each user)
$uniqueUsers = $results | 
    Where-Object { -not [string]::IsNullOrWhiteSpace($_.DeviceUser) } |  # Filter out empty entries
    Sort-Object TimeCreated -Descending | 
    Group-Object DeviceUser | 
    Where-Object { $_.Name -ne "" } |  # Filter out empty groups
    ForEach-Object { $_.Group | Select-Object -First 1 }

# Get accurate count
$totalUsers = ($uniqueUsers | Where-Object { -not [string]::IsNullOrWhiteSpace($_.DeviceUser) }).Count

Write-Host "Total unique users needing password change: $totalUsers" -ForegroundColor Cyan


$tableRows = $uniqueUsers | Sort-Object LastAttempt -Descending | ForEach-Object {
    @"
    <tr>
        <td>$($_.DeviceUser)</td>
        <td>$($_.LastAttempt)</td>
        <td class="failure-code">$($_.ReasonCode)</td>
        <td>$($_.Reason)</td>
        <td>$($_.AuthType)</td>
        <td>$($_.CRPPolicy)</td>
    </tr>
"@
}

$html = @"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Password Change Required Report</title>
    $style
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>Users Required to Change Password</h1>
            <div class="timestamp">Report generated: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")</div>
            <div class="timestamp">Generated by: $($currentUser.Name)</div>
        </div>
        
        <div class="health-cards">
            <div class="card card-yellow">
                <div class="card-title">Users Needing Password Change</div>
                <div class="card-value">$totalUsers</div>
            </div>
        </div>

        <div class="table-container">
            <table>
                <thead>
                    <tr>
                        <th>User Account</th>
                        <th>Last Failed Attempt</th>
                        <th>Reason Code</th>
                        <th>Reason</th>
                        <th>Auth Type</th>
                        <th>Connection Policy</th>
                    </tr>
                </thead>
                <tbody>
                    $tableRows
                </tbody>
            </table>
        </div>
    </div>
</body>
</html>
"@

$ReportPath = Join-Path $scriptPath $ReportName
$html | Out-File $ReportPath -Encoding UTF8
Write-Host "`nReport generated successfully at: $ReportPath" -ForegroundColor Green
Write-Host "Script completed at: $(Get-Date)" -ForegroundColor Green

This will then create the html file called password-change-report.html as you can see below:


Extract UPN for further remediation

If you wish to engage the users in this situation then first you need to extract the UPN from the samAccountName, when you have this you can send them an e-mail to start with, so first lets get the UPN.

Add this to the end of the previous script, this will also normalise the data so there is only one valid UPN in the exported file:

Write-Host "`nExporting UPNs to file..." -ForegroundColor Yellow

# Clear the UPN file if it exists
if (Test-Path "$scriptPath\upn_report.txt") {
    Remove-Item "$scriptPath\upn_report.txt"
}

# Get unique users and their UPNs
$uniqueUsers | 
    Select-Object -ExpandProperty DeviceUser -Unique | 
    ForEach-Object {
        # Extract username from DOMAIN\username format
        $username = $_.Split('\')[1]
        
        try {
            Get-ADUser -Identity $username -Properties UserPrincipalName | 
                Select-Object -ExpandProperty UserPrincipalName |
                Out-File -FilePath "$scriptPath\upn_report.txt" -Append
        }
        catch {
            Write-Host "Could not get UPN for user: $username" -ForegroundColor Red
        }
    }

Write-Host "UPN report generated: $scriptPath\upn_report.txt" -ForegroundColor Green

This will then export a file called upn_report.txt as you can see below:


This file will contain the UPN of the user affected, now we can move on to sending them an email (remember that email on Outlook for mobile devices will work as this uses a token) you can also send them a notification via InTune if that indeed is your MDM solution.

e-mail Notification

The e-mail notification will look like this when the e-mail has been sent from the script, this email will only be sent to the users in the upn_report.txt file:


Script : email-reminder.ps1

# PowerShell Script for Password Expiration Email Notifications

# Email configuration
$smtpServer = "smtp.bear.local"
$fromAddress = "passwords@croucher.cloud"
$subject = "Your Password Has Expired"

$emailTemplate = @"
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        /* Reset styles for email clients */
        body, html {
            margin: 0;
            padding: 0;
            width: 100% !important;
            -webkit-text-size-adjust: 100%;
            -ms-text-size-adjust: 100%;
        }
        
        /* Base styles */
        body {
            font-family: 'Segoe UI', Arial, sans-serif;
            line-height: 1.6;
            color: #333333;
        }
        
        /* Container with max-width and responsive padding */
        .container {
            max-width: 600px;
            width: 100% !important;
            margin: 0 auto;
            padding: 20px;
            box-sizing: border-box;
            background-color: #ffffff;
            border-radius: 8px;
            border: 1px solid #e0e0e0;
        }
        
        /* Responsive header */
        .header {
            text-align: center;
            padding-bottom: 20px;
            border-bottom: 2px solid #f0f0f0;
        }
        
        .header img {
            max-width: 150px;
            width: 100%;
            height: auto;
            margin: 0 auto;
        }
        
        /* Content area with responsive padding */
        .content {
            padding: 20px 0;
            font-size: 16px;
        }
        
        /* Responsive footer */
        .footer {
            text-align: center;
            padding-top: 20px;
            border-top: 2px solid #f0f0f0;
            font-size: 14px;
            color: #666666;
        }
        
        /* Responsive button */
        .button {
            display: inline-block;
            padding: 12px 24px;
            background-color: #0078d4;
            color: #ffffff !important;
            text-decoration: none;
            border-radius: 4px;
            margin: 20px 0;
            font-size: 16px;
            text-align: center;
            min-width: 200px;
        }
        
        /* List styling */
        ul {
            padding-left: 20px;
            margin: 15px 0;
        }
        
        li {
            margin-bottom: 8px;
        }
        
        /* Media query for mobile devices */
        @media screen and (max-width: 600px) {
            .container {
                width: 100% !important;
                padding: 10px;
            }
            
            .content {
                padding: 15px 10px;
            }
            
            .button {
                display: block;
                margin: 20px auto;
                width: 80%;
                max-width: 300px;
            }
            
            h2 {
                font-size: 20px;
                margin: 10px 0;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <img src="<header_image_url>" alt="Header Logo">
            <h2>Password Update Required</h2>
        </div>
        <div class="content">
            <p>We noticed your password has expired.</p>
            <p>You may notice you have limited access to certain resources with your password expired.</p>
            <center>
                <a href="https://passwordreset.microsoftonline.com/" class="button">Change Password</a>
            </center>
            <p>Remember, a strong password is like a good joke - complex enough to be interesting, but memorable enough to not forget! 😉</p>
            <p>Quick password tips:</p>
            <ul>
                <li>Mix uppercase and lowercase letters</li>
                <li>Include numbers and special characters</li>
                <li>Make it at least 15 characters long</li>
                <li>Avoid using common phrases or words</li>
            </ul>
        </div>
        <div class="footer">
            <p>Need help? Contact the Service Desk<br>
            We're here to help (between 8am - 4pm)</p>
        </div>
    </div>
</body>
</html>
"@

# Function to send email
function Send-PasswordExpirationEmail {
    param (
        [string]$userUPN
    )
    
    try {
        $emailParams = @{
            From = $fromAddress
            To = $userUPN
            Subject = $subject
            Body = $emailTemplate
            BodyAsHtml = $true
            SmtpServer = $smtpServer
        }
        
        Send-MailMessage @emailParams
        Write-Host "Successfully sent email to $userUPN" -ForegroundColor Green
    }
    catch {
        Write-Host "Failed to send email to $userUPN`: $($_.Exception.Message)" -ForegroundColor Red
    }
}

# Read UPN file and send emails
try {
    $upnList = Get-Content "upn_report.txt"
    
    foreach ($upn in $upnList) {
        if (![string]::IsNullOrWhiteSpace($upn)) {
            Send-PasswordExpirationEmail -userUPN $upn.Trim()
            # Add a small delay between emails to prevent overwhelming the mail server
            Start-Sleep -Seconds 2
        }
    }
}
catch {
    Write-Host "Error processing UPN file: $_" -ForegroundColor Red
}

Mobile Notification (via Intune)

If you would rather have a mobile notification which will look like a popup on the users device which will look like this:


Then you can use the script below, you need to ensure you have the relevant permissions then the script performs the follow actions:
  1. Looks in the upn_report.txt for the users UPN
  2. Retrieves the managed device(s) from Intune
  3. Prepares the Notification message
  4. Send Notification to the selected devices

Script : intune-Notification.ps1

# Connect to Microsoft Graph
Connect-MgGraph -Scopes "DeviceManagementManagedDevices.PrivilegedOperations.All"

# Read UPNs from the file
$userUPNs = Get-Content -Path "upn_report.txt"

# Get the device IDs for each user's iOS devices
$deviceIds = @()
foreach ($upn in $userUPNs) {
    $devices = Get-MgUserManagedDevice -UserId $upn | Where-Object { $_.OperatingSystem -eq "iOS" }
    $deviceIds += $devices.Id
}

# Set up notification parameters
$params = @{
    NotificationTitle = "Urgent Notification"
    NotificationBody = "Your password has expired and needs to be updated immedaitely!"
    DevicesToNotify = $deviceIds
}

# Send notification
Invoke-MgDeviceManagementSendCustomNotificationToCompanyPortal -BodyParameter $params

Previous Post Next Post

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