PowerShell Login Script: Drive Mapping and Greeting Popup

This script is a modern PowerShell replacement for the classic VBScript logon script. It maps network drives for all users, maps additional drives based on AD group membership, and shows a time-of-day greeting popup at login. Deploy it via GPO under User Configuration > Policies > Windows Settings > Scripts (Logon), PowerShell Scripts tab.

How It Works

Drive Map Definitions

Two data structures control what gets mapped. $PublicDrives is a hashtable of drive letters to UNC paths that every user gets. $GroupDrives is an array of hashtables that ties a drive letter and UNC path to a specific AD group. Edit these sections to match your environment before deploying.

Test-GroupMembership

Checks whether the current user belongs to a given AD group by reading the Windows logon token directly via WindowsPrincipal and WindowsIdentity. No ActiveDirectory module, no LDAP query, no network round-trip to a DC. Because it reads the token, nested group membership works correctly without any extra logic. If the group name doesn’t resolve (typo, deleted group), it catches the exception and returns $false rather than blowing up.

Set-MappedDrive

Handles the full drive mapping lifecycle. It checks whether the letter is already mapped with Get-SmbMapping. If it is and points to the right path, it does nothing. If it’s mapped to a different path, it removes the old mapping first. Then it maps the drive with New-SmbMapping using -Persistent $false, which keeps it tied to the session rather than writing it into the user’s profile permanently. Failures are caught and written as warnings instead of stopping the script.

Show-Greeting

Fires a WScript.Shell popup with a time-appropriate greeting and the current date and time. The popup auto-dismisses after 7 seconds, so it won’t block the user if they walk away from the machine at login.

Main Execution Block

Group-restricted drives are processed first. Each entry in $GroupDrives is checked with Test-GroupMembership, and the drive is only mapped if the user qualifies. Then all public drives are mapped in a second loop. The greeting fires last.

Usage

Deployment via GPO

Link the script to a GPO targeting the users who should receive it.

User Configuration > Policies > Windows Settings > Scripts (Logon) > PowerShell Scripts tab

Add the script file there. The machine must allow PowerShell scripts to run. Set the execution policy to at least RemoteSigned via GPO if you haven’t already.

Computer Configuration > Policies > Administrative Templates > Windows Components > Windows PowerShell > Turn on Script Execution

Required Permissions

  • No admin rights needed.
  • The user running the script only needs read access to the UNC paths being mapped.
  • No ActiveDirectory PowerShell module required.

Minimum Requirements

PowerShell 5.1 (#Requires -Version 5.1 is set at the top). The SmbMapping cmdlets and WScript.Shell COM object are available on any modern Windows client. Domain-joined machines only.

Customizing Drive Maps

Edit $PublicDrives and $GroupDrives at the top of the script before deploying. For group drives, use the fully qualified group name in DOMAINGroup Name format.

$GroupDrives = @(
    @{ Group = "DOMAIN\Finance Team"; Letter = 'F'; Path = '\FILESERVER\Finance' }
    @{ Group = "DOMAIN\IT Admins";    Letter = 'H'; Path = '\FILESERVER\IT' }
)Code language: PowerShell (powershell)

Caveats

Logon Token vs. Real-Time Group Membership

Test-GroupMembership reads the token from the current logon session. If a user was added to a group after their last login, the script won’t see it until they log out and back in. This is standard Windows behavior, not a bug in the script.

Drive Letter Conflicts

Set-MappedDrive removes and remaps a drive if it’s mapped to the wrong path. If something else (another script, a manually mapped drive) has that letter claimed before this script runs, it will get overwritten without prompting.

-Persistent $false

Drives mapped with -Persistent $false don’t survive a reboot on their own. They’re gone when the session ends. That’s intentional here since the GPO runs this script every login. Don’t change it to $true unless you want stale mappings building up in the profile.

Popup Behavior

WScript.Shell popups are modal on older systems. On Windows 10 and 11 they generally don’t block the shell, but on some setups or with fast login scenarios, the 7-second popup might briefly annoy users or interfere with startup applications. Drop Show-Greeting entirely if that’s a concern.

SMB Availability at Logon

If the file server isn’t reachable when the script runs (VPN not up yet, network timing issues), New-SmbMapping will fail. The catch block writes a warning, which goes nowhere visible in a GPO logon context unless you redirect output to a log file. Consider adding file-based logging if you need visibility into mapping failures.

GPP vs. Script

The script comments this correctly. If all you need is drive mapping without conditional logic, Group Policy Preferences Drive Maps with item-level targeting is simpler and has no script execution dependency. Use this script when you need logic that GPP can’t express.

Full Script

<#
.SYNOPSIS
    Network logon script in PowerShell - modern equivalent of the classic
    VBScript logon script.

.DESCRIPTION
    Maps network drives (globally and per AD group) and shows a brief
    greeting popup. Deploy via GPO:
    User Configuration > Policies > Windows Settings > Scripts (Logon) >
    PowerShell Scripts tab.

    Group membership is read from the user's logon token, so nested
    groups are handled correctly - no ActiveDirectory module or extra
    AD queries needed.

    Note: for plain drive mapping, Group Policy Preferences Drive Maps
    (with item-level targeting) needs no script at all. Use a script when
    you need logic GPP can't express.

.NOTES
    Time sync is intentionally absent - domain-joined machines sync
    automatically via the w32time domain hierarchy. The old
    'net time /DOMAIN /SET' trick is obsolete and required admin rights.
#>

#Requires -Version 5.1

$DomainName = 'DOMAIN_NAME_1'

#=======================================================
# Drive map definitions - edit these for your network
#=======================================================

# Drives mapped for everyone
$PublicDrives = @{
    # Spartan drives
    'L' = '\SERVER01\share'
    'M' = '\SERVER02\share'
    'N' = '\SERVER03\share'
    # Lamont drives
    'O' = '\SERVER04\share'
    'P' = '\SERVER05\share'
    'Q' = '\SERVER04\share2'
    'R' = '\SERVER07\share'
    'S' = '\SERVER08\share'
    'X' = '\SERVER09\share'
    'Y' = '\SERVER01\share0'
    'Z' = '\SERVER01\share1'
    'K' = '\SERVER01\share2'
    # Skynet drives
    'T' = '\SERVER01\share3'
    'U' = '\SERVER01\share4'
}

# Drives mapped only for members of a given AD group (nested membership counts)
$GroupDrives = @(
    @{ Group = "$DomainName\IT Admins"; Letter = 'H'; Path = '\SERVER01\share5' }
)

#=======================================================
# Functions
#=======================================================

function Test-GroupMembership {
    param ([string]$GroupName)

    # Checks the logon token rather than querying AD - fast, offline-capable,
    # and includes nested group membership.
    $principal = [host1.example.com]::new(
        [host2.example.com]::GetCurrent())
    try {
        return $principal.IsInRole($GroupName)
    }
    catch {
        # Group doesn't resolve (typo, deleted group) - treat as not a member
        return $false
    }
}

function Set-MappedDrive {
    param (
        [string]$Letter,
        [string]$Path
    )

    $existing = Get-SmbMapping -LocalPath "${Letter}:" -ErrorAction SilentlyContinue
    if ($existing) {
        if ($existing.RemotePath -eq $Path) { return }   # already correct
        Remove-SmbMapping -LocalPath "${Letter}:" -Force -UpdateProfile
    }

    try {
        New-SmbMapping -LocalPath "${Letter}:" -RemotePath $Path -Persistent $false -ErrorAction Stop | Out-Null
    }
    catch {
        Write-Warning "Failed to map ${Letter}: to $Path - $($_.Exception.Message)"
    }
}

function Show-Greeting {
    param ([string]$UserName)

    $popupSeconds = 7
    $greeting = switch ((Get-Date).Hour) {
        { $_ -lt 12 } { 'Good morning!'; break }
        { $_ -lt 17 } { 'Good afternoon!'; break }
        default       { 'Good evening!' }
    }

    $message = "$greeting $UserName, welcome to $DomainName!`n" +
               "It is $(Get-Date -Format 'h:mm:ss tt on dddd, MMMM d, yyyy')"

    (New-Object -ComObject WScript.Shell).Popup($message, $popupSeconds, 'Login Script',3) | Out-Null
}

#=======================================================
# Main
#=======================================================

foreach ($drive in $GroupDrives) {
    if (Test-GroupMembership -GroupName $drive.Group) {
        Set-MappedDrive -Letter $drive.Letter -Path $drive.Path
    }
}

foreach ($letter in $PublicDrives.Keys) {
    Set-MappedDrive -Letter $letter -Path $PublicDrives[$letter]
}

Show-Greeting -UserName $env:USERNAMECode language: PowerShell (powershell)