~/2025/11/26/powershell-windows-server-disable-smbv1-fleet-wide-and-verify-it-stuck.md

PowerShell: Windows Server – Disable SMBv1 Fleet-Wide and Verify

---
author: 
date: 
read: 4 min
in:   [ps, scripts]
tags: [powershell, windows]
---

$ grep -n '^#' post.md

SMBv1 isn't installed by default on Windows Server 2019 and later, but "not installed by default" and "not installed anywhere" are two different statements once you have a server fleet with years of in-place upgrades behind it. Microsoft's position is blunt: SMBv1 "has significant security vulnerabilities, and we strongly encourage you not to use it," and if something still needs it, update that thing rather than reinstall SMBv1. Disabling it is one of the highest-value changes you can make, as long as you first find out who's still using it. This script does all three steps against a list of servers: it turns on SMBv1 access auditing and counts the audit events, it disables the SMBv1 server and the SMB1Protocol optional feature, and because disabling the feature only finishes at restart, it reports which servers are clean, which are pending a restart, and which failed.

Requirements

  • Windows PowerShell 5.1 or later on the admin workstation, with PowerShell remoting (WinRM) reachable on every target.
  • Windows Server 2012 R2 or later on the targets. That's where the SMB1Protocol optional feature and the SmbShare cmdlets (Get-SmbServerConfiguration, Set-SmbServerConfiguration) exist. Server 2008 R2 uses a registry value instead (see Notes).
  • Local administrator rights on each target.
  • Time. Run the -Audit pass and leave it for long enough to cover month-end jobs, backups and scanners before you run the disable pass.

Parameters

NameTypeRequiredDescription
-ComputerNameStringNoServers to process. If omitted, -ComputerListPath is used.
-ComputerListPathStringNoText file with one server name per line.
-AuditSwitchNoTurns on AuditSmb1Access and reports current state and event 3000 counts. Changes nothing else.
-VerifySwitchNoReports current state and event 3000 counts only. Changes nothing.
-AuditDaysIntNoHow many days of Microsoft-Windows-SMBServer/Audit events to count. Default 14.
-OutputPathStringNoCSV of per-server results. Default .\smbv1-status.csv.
-ThrottleLimitIntNoMaximum concurrent remote connections. Default 16.

With neither -Audit nor -Verify, the script disables SMBv1.

Usage

Turn on auditing across the fleet and see where SMBv1 is still installed:

powershell
.\Disable-Smb1Fleetwide.ps1 -ComputerListPath C:\Lists\servers.txt -Audit

A couple of weeks later, check who has been connecting with SMBv1 (event ID 3000):

powershell
.\Disable-Smb1Fleetwide.ps1 -ComputerListPath C:\Lists\servers.txt -Verify -AuditDays 30

Look at the actual clients on a server that logged events before you break them:

powershell
Get-WinEvent -ComputerName SRV-FILE01 -FilterHashtable @{ LogName = "Microsoft-Windows-SMBServer/Audit"; Id = 3000 } -MaxEvents 20 |
    Format-List -Property TimeCreated, Message

Disable SMBv1 on the servers that came back clean, then see which need a restart:

powershell
.\Disable-Smb1Fleetwide.ps1 -ComputerListPath C:\Lists\servers-clean.txt

Re-run in verify mode after the maintenance window's restarts to confirm it stuck:

powershell
.\Disable-Smb1Fleetwide.ps1 -ComputerListPath C:\Lists\servers-clean.txt -Verify -OutputPath C:\Reports\smbv1-post-restart.csv

Sample console output from a disable run:

text
Processing 3 server(s), throttle limit 16, mode Disable ...
SRV-FILE01: disabled, restart pending (feature DisablePending, server SMB1 off)
SRV-FILE02: already disabled, nothing to do
WARNING: Unreachable: SRV-PRINT03 - WinRM cannot complete the operation. Verify that the specified computer name is valid, that the computer is accessible over the network, and that a firewall exception for the WinRM service is enabled and allows access from this computer.
Results written to .\smbv1-status.csv

Script

powershell
<#
.SYNOPSIS
    Audits, disables and verifies SMBv1 across a list of Windows servers.
.DESCRIPTION
    For each target server, reads the SMB1Protocol optional feature state, the
    SMB server's EnableSMB1Protocol and AuditSmb1Access settings, and the number
    of event ID 3000 entries (SMBv1 client access) in the
    Microsoft-Windows-SMBServer/Audit log. -Audit also turns auditing on.
    Without -Audit or -Verify, the script turns off the SMBv1 server
    (Set-SmbServerConfiguration, takes effect without a restart), disables the
    SMB1Protocol feature with -NoRestart, re-reads the feature state and records
    whether a restart is pending. Results go to the console and a CSV.
.PARAMETER ComputerName
    One or more servers to process.
.PARAMETER ComputerListPath
    Text file with one server name per line, used when -ComputerName is not supplied.
.PARAMETER Audit
    Turns on AuditSmb1Access and reports state. Makes no other change.
.PARAMETER Verify
    Reports state only. Makes no changes.
.PARAMETER AuditDays
    Days of SMBServer/Audit events to count. Default 14.
.PARAMETER OutputPath
    CSV of per-server results. Default .\smbv1-status.csv.
.PARAMETER ThrottleLimit
    Maximum concurrent remote connections. Default 16.
.EXAMPLE
    .\Disable-Smb1Fleetwide.ps1 -ComputerListPath C:\Lists\servers.txt -Audit
.EXAMPLE
    .\Disable-Smb1Fleetwide.ps1 -ComputerListPath C:\Lists\servers.txt
.NOTES
    Author  : Thomas Lasswell (https://www.techcolumnist.com)
    Version : 1.0 (2025-11-26)
    Requires: PowerShell remoting on targets; Windows Server 2012 R2 or later; local admin
.LINK
    https://learn.microsoft.com/en-us/windows-server/storage/file-server/troubleshoot/detect-enable-and-disable-smbv1-v2-v3
#>
[CmdletBinding()]
param (
    [string[]]$ComputerName,

    [string]$ComputerListPath,

    [switch]$Audit,

    [switch]$Verify,

    [ValidateRange(1, 365)]
    [int]$AuditDays = 14,

    [string]$OutputPath = ".\smbv1-status.csv",

    [int]$ThrottleLimit = 16
)

if ($Audit -and $Verify) {
    throw "Use -Audit or -Verify, not both."
}

if (-not $ComputerName) {
    if (-not $ComputerListPath -or -not (Test-Path -Path $ComputerListPath)) {
        throw "Supply -ComputerName or a valid -ComputerListPath."
    }

    $ComputerName = Get-Content -Path $ComputerListPath | Where-Object { $_.Trim() -ne "" }
}

$mode = if ($Audit) { "Audit" } elseif ($Verify) { "Verify" } else { "Disable" }

Write-Host "Processing $(@($ComputerName).Count) server(s), throttle limit $ThrottleLimit, mode $mode ..."

# Runs on each remote server. Mode and AuditDays arrive through -ArgumentList.
$smb1Script = {
    param ($Mode, $AuditDays)

    $result = [ordered]@{
        StartingFeature    = $null
        EndingFeature      = $null
        ServerSmb1Enabled  = $null
        AuditEnabled       = $null
        Smb1AccessEvents   = $null
        Action             = "none"
        RestartNeeded      = $false
        Error              = $null
    }

    try {
        $feature = Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol -ErrorAction Stop
        $result.StartingFeature = [string]$feature.State

        if ($Mode -eq "Audit") {
            Set-SmbServerConfiguration -AuditSmb1Access $true -Force -ErrorAction Stop
            $result.Action = "audit enabled"
        }

        if ($Mode -eq "Disable") {
            # Server side first: takes effect immediately, no restart needed.
            if ((Get-SmbServerConfiguration).EnableSMB1Protocol) {
                Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force -ErrorAction Stop
                $result.Action = "server SMB1 off"
            }

            # Then disable the feature (SMBv1 client and server). Finishes at restart.
            if ($feature.State -ne "Disabled") {
                $image = Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol -NoRestart -ErrorAction Stop
                $result.RestartNeeded = [bool]$image.RestartNeeded
                $result.Action = "disabled"
            }
        }

        $config = Get-SmbServerConfiguration
        $result.ServerSmb1Enabled = $config.EnableSMB1Protocol
        $result.AuditEnabled = $config.AuditSmb1Access
        $result.EndingFeature = [string](Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol).State

        $filter = @{
            LogName   = "Microsoft-Windows-SMBServer/Audit"
            Id        = 3000
            StartTime = (Get-Date).AddDays(-$AuditDays)
        }
        $result.Smb1AccessEvents = @(Get-WinEvent -FilterHashtable $filter -ErrorAction SilentlyContinue).Count
    } catch {
        $result.Action = "failed"
        $result.Error = $_.Exception.Message
    }

    [PSCustomObject]$result
}

$invokeParams = @{
    ComputerName  = $ComputerName
    ScriptBlock   = $smb1Script
    ArgumentList  = $mode, $AuditDays
    ThrottleLimit = $ThrottleLimit
    ErrorAction   = "SilentlyContinue"
    ErrorVariable = "remoteErrors"
}

$results = Invoke-Command @invokeParams

foreach ($result in ($results | Sort-Object -Property PSComputerName)) {
    $target = $result.PSComputerName

    if ($result.Error) {
        Write-Host "$target`: failed - $($result.Error)"
    } elseif ($result.Action -eq "disabled") {
        $state = if ($result.RestartNeeded) { "restart pending" } else { "no restart needed" }
        Write-Host "$target`: disabled, $state (feature $($result.EndingFeature), server SMB1 $(if ($result.ServerSmb1Enabled) { 'on' } else { 'off' }))"
    } elseif ($result.EndingFeature -eq "Disabled" -and -not $result.ServerSmb1Enabled) {
        Write-Host "$target`: already disabled, nothing to do"
    } else {
        Write-Host "$target`: SMBv1 feature $($result.EndingFeature), server SMB1 enabled $($result.ServerSmb1Enabled), audit $($result.AuditEnabled), $($result.Smb1AccessEvents) access event(s) in $AuditDays days"
    }
}

$results |
    Select-Object -Property PSComputerName, StartingFeature, EndingFeature, ServerSmb1Enabled, AuditEnabled, Smb1AccessEvents, Action, RestartNeeded, Error |
    Export-Csv -Path $OutputPath -NoTypeInformation

foreach ($failure in $remoteErrors) {
    Write-Warning "Unreachable: $($failure.TargetObject) - $($failure.Exception.Message)"
}

Write-Host "Results written to $OutputPath"

Notes

  • Two switches, two effects. Set-SmbServerConfiguration -EnableSMB1Protocol $false stops the SMB server from accepting SMBv1, and Microsoft notes you "don't have to restart the computer" after it. Disable-WindowsOptionalFeature -FeatureName SMB1Protocol disables the SMBv1 feature, client and server, and needs a restart to finish, which is why the script records RestartNeeded and the post-change feature state separately. Without -Remove it leaves the payload files on disk, so the feature can be re-enabled without installation media; add -Remove only if you want the binaries gone too. Don't treat a server as done until a -Verify pass after the restart shows EndingFeature as Disabled.
  • Auditing only sees the server side. With AuditSmb1Access on, every SMBv1 connection attempt to that server logs event ID 3000 in Microsoft-Windows-SMBServer/Audit; open the events (the Get-WinEvent example above) to see the client details. It tells you nothing about servers that initiate SMBv1 connections outward, for example to an old NAS or a scan-to-folder copier. Check those devices from the other side or with the vendor.
  • Domain controllers. Microsoft warns that "some systems require access to the SYSVOL folder or other file shares but don't support SMBv2 or SMBv3," such as legacy Windows and older Linux or partner systems. Audit DCs longest and do them last. Microsoft keeps an SMB1 Product Clearinghouse (https://aka.ms/stillneedssmb1) of products that required SMBv1 and the updates that remove the requirement.
  • Group Policy instead of a script. For ongoing enforcement Microsoft documents registry preference items: HKLM\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters\SMB1 = 0 (REG_DWORD) for the server, and for the client, HKLM\SYSTEM\CurrentControlSet\services\mrxsmb10\Start = 4 plus LanmanWorkstation\DependOnService replaced with Bowser, MRxSmb20, NSI. Both need a restart. That's also the method for Server 2008 R2 and Windows 7, which have no SMB1Protocol feature.
  • Don't confuse this with signing. On Windows 11 24H2 and Windows Server 2025, SMB signing is required by default, which can break connections to third-party SMB servers that don't sign. Microsoft is explicit that changing which SMB versions are enabled doesn't change the signing requirement, so don't re-enable SMBv1 (or disable signing) to "fix" those failures.
  • I stage this in batches by server role rather than all at once: file and print servers first (where legacy SMBv1 clients are most likely to show up in the audit log), then application servers, and domain controllers last, with a week between batches to let any breakage show up in help desk tickets before the blast radius grows.

Source