#Requires -Version 5.1 # Run in Windows PowerShell 5.1 or PowerShell 7 as Administrator. $ErrorActionPreference = 'Stop' $shareCreated = $false $userCreated = $false $folderCreated = $false $firewallCreated = $false $ruleName = 'LANShare-LAN-SMB-In' function Read-NonEmpty([string]$Prompt, [string]$Default) { while ($true) { $value = Read-Host "$Prompt [$Default] (leave blank to use default)" if ([string]::IsNullOrWhiteSpace($value)) { $value = $Default } if (-not [string]::IsNullOrWhiteSpace($value)) { return $value.Trim() } } } function Get-IPv4 { @(Get-NetIPConfiguration | Where-Object { $_.IPv4DefaultGateway -and $_.IPv4Address } | ForEach-Object { $_.IPv4Address.IPAddress } | Select-Object -Unique) } try { $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = [Security.Principal.WindowsPrincipal]::new($identity) if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { throw 'Run PowerShell as Administrator, then execute this script again.' } Write-Host '\n=== Secure LAN Share Setup ===' -ForegroundColor Cyan $shareName = Read-NonEmpty 'SMB share name (letters, digits, hyphen, underscore)' 'SharedFiles' if ($shareName -notmatch '^[A-Za-z0-9_-]{1,40}$') { throw 'Invalid share name. Use letters, digits, hyphens or underscores only (1-40 characters).' } $userName = Read-NonEmpty 'New LOCAL share username (letters, digits, hyphen, underscore)' 'FileShareUser' if ($userName -notmatch '^[A-Za-z][A-Za-z0-9_-]{0,19}$') { throw 'Invalid username. Start with a letter; use up to 20 letters, digits, hyphens or underscores.' } $folderPath = Read-NonEmpty 'Local folder to create/share (new, empty folder recommended)' 'C:\SharedFiles' $folderPath = [IO.Path]::GetFullPath($folderPath) if ($folderPath -notmatch '^[A-Za-z]:\\' -or $folderPath -match '^[A-Za-z]:\\$') { throw 'Choose a local folder beneath a drive root, e.g. C:\SharedFiles.' } if ($folderPath.StartsWith('\\')) { throw 'UNC folders are not supported.' } if (Get-LocalUser -Name $userName -ErrorAction SilentlyContinue) { throw "Local account '$userName' already exists. Choose a new username." } if (Get-SmbShare -Name $shareName -ErrorAction SilentlyContinue) { throw "SMB share '$shareName' already exists. Choose a new share name." } if (Test-Path -LiteralPath $folderPath) { throw "Folder '$folderPath' already exists. Use a NEW folder so cleanup never deletes preexisting data." } $ips = Get-IPv4 Write-Host ('Detected host IPv4: ' + ($ips -join ', ')) $pass = Read-Host 'Enter a strong password for the new share account' -AsSecureString if ($pass.Length -lt 12) { throw 'Use a password of at least 12 characters.' } $confirm = Read-Host 'Create account, folder, SMB share and LAN firewall rule? (Y/N)' if ($confirm -notmatch '^[Yy]$') { Write-Host 'Cancelled. No changes made.'; return } New-LocalUser -Name $userName -Password $pass -Description 'Dedicated LAN SMB share account' -PasswordNeverExpires | Out-Null $userCreated = $true New-Item -ItemType Directory -Path $folderPath | Out-Null $folderCreated = $true $account = "$env:COMPUTERNAME\$userName" # Inheritance is disabled only on a brand-new empty folder. Keep SYSTEM and Administrators. $acl = Get-Acl -LiteralPath $folderPath $acl.SetAccessRuleProtection($true, $false) foreach ($entry in @( @{ Name = 'SYSTEM'; Rights = 'FullControl' }, @{ Name = 'BUILTIN\Administrators'; Rights = 'FullControl' }, @{ Name = $account; Rights = 'Modify' } )) { $rule = [Security.AccessControl.FileSystemAccessRule]::new($entry.Name, $entry.Rights, [Security.AccessControl.InheritanceFlags]'ContainerInherit, ObjectInherit', [Security.AccessControl.PropagationFlags]::None, [Security.AccessControl.AccessControlType]::Allow) $acl.AddAccessRule($rule) } Set-Acl -LiteralPath $folderPath -AclObject $acl New-SmbShare -Name $shareName -Path $folderPath -ChangeAccess $account -EncryptData $true | Out-Null $shareCreated = $true # Allow SMB from the local subnet on Private networks; no client IP is requested. $ruleId = "$ruleName-$shareName" New-NetFirewallRule -Name $ruleId -DisplayName "LAN Share $shareName - local subnet" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 445 -RemoteAddress LocalSubnet -Profile Private | Out-Null $firewallCreated = $true $ipText = if ($ips.Count -gt 0) { $ips[0] } else { '' } $unc = "\\$ipText\$shareName" Write-Host '\n========== SETUP COMPLETE ==========' -ForegroundColor Green Write-Host "Host computer : $env:COMPUTERNAME" Write-Host "Folder : $folderPath" Write-Host "Share : $shareName" Write-Host "Connect to : $unc" Write-Host "Username : $account" Write-Host 'Password : (the password entered; not printed or saved)' Write-Host 'Encryption : SMB encryption required' Write-Host '\nCLIENT STEPS:' -ForegroundColor Cyan Write-Host "1. On the client, press Win+R and enter: $unc" Write-Host "2. Enter username: $account" Write-Host '3. Enter the password you chose on the host.' Write-Host '4. Optional: File Explorer > This PC > Map network drive, then use the same UNC path.' Write-Host '\nIMPORTANT:' -ForegroundColor Yellow Write-Host 'Set the host network to Private. Keep Password protected sharing ON.' Write-Host 'SMB firewall access is allowed from the local subnet on Private networks (not restricted to one client IP).' Write-Host 'Existing broader SMB firewall rules are not modified.' Write-Host 'Keep the host awake while sharing. Do not expose TCP 445 to the Internet.' Write-Host '\nCLEANUP:' Write-Host ".\Remove-LANShare.ps1 -ShareName '$shareName' -UserName '$userName' -FolderPath '$folderPath'" Write-Host '====================================' } catch { Write-Host "`nSETUP FAILED: $($_.Exception.Message)" -ForegroundColor Red Write-Host 'Any resources created before the failure may remain. Run the cleanup script with the values you entered.' -ForegroundColor Yellow exit 1 }