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 tabAdd 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.
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)