Instantly share code, notes, and snippets.
Created
October 27, 2025 16:23
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save bitmvr/691288606fde9b5de20f9f1f632de937 to your computer and use it in GitHub Desktop.
Interactive File Share Manager for Windows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Service User and SMB Share Management Script for Windows | |
| # Must be run as Administrator | |
| # Note: This is not enterprise grade and it's not meant to be. | |
| # This is to assist small offices and home labs in | |
| # being productive. | |
| #Requires -RunAsAdministrator | |
| # Function to write colored output | |
| function Write-Status { | |
| param( | |
| [string]$Message, | |
| [ValidateSet('Info', 'Success', 'Warning', 'Error')] | |
| [string]$Type = 'Info' | |
| ) | |
| $color = switch ($Type) { | |
| 'Info' { 'Cyan' } | |
| 'Success' { 'Green' } | |
| 'Warning' { 'Yellow' } | |
| 'Error' { 'Red' } | |
| } | |
| Write-Host "[$Type] $Message" -ForegroundColor $color | |
| } | |
| # Function to show main menu | |
| function Show-MainMenu { | |
| Write-Host "" | |
| Write-Host "========================================" -ForegroundColor Cyan | |
| Write-Host " File Server Management Menu" -ForegroundColor Cyan | |
| Write-Host "========================================" -ForegroundColor Cyan | |
| Write-Host " [1] Create new service user" -ForegroundColor White | |
| Write-Host " [2] Add share to user" -ForegroundColor White | |
| Write-Host " [3] List existing users" -ForegroundColor White | |
| Write-Host " [4] List existing shares" -ForegroundColor White | |
| Write-Host " [5] Exit" -ForegroundColor White | |
| Write-Host "========================================" -ForegroundColor Cyan | |
| Write-Host "" | |
| $choice = Read-Host "Select an option (1-5)" | |
| return $choice | |
| } | |
| # Function to get or create user | |
| function Get-OrCreateUser { | |
| param( | |
| [bool]$CreateNew = $true | |
| ) | |
| if ($CreateNew) { | |
| # Create new user | |
| $svcUser = Read-Host "Enter service account username (e.g., svc-user)" | |
| # Check if user already exists | |
| $existingUser = Get-LocalUser -Name $svcUser -ErrorAction SilentlyContinue | |
| if ($existingUser) { | |
| Write-Status "User '$svcUser' already exists" -Type Warning | |
| $resetPwd = Read-Host "Would you like to reset the password? (Y/N)" | |
| if ($resetPwd -ne 'Y') { | |
| return $svcUser | |
| } | |
| } | |
| $svcPass = Read-Host "Enter password for $svcUser" -AsSecureString | |
| $svcPassConfirm = Read-Host "Confirm password" -AsSecureString | |
| # Compare passwords | |
| $pwd1 = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($svcPass)) | |
| $pwd2 = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($svcPassConfirm)) | |
| if ($pwd1 -ne $pwd2) { | |
| Write-Status "Passwords do not match. Aborting." -Type Error | |
| return $null | |
| } | |
| if ($existingUser) { | |
| # Update existing user password | |
| try { | |
| $existingUser | Set-LocalUser -Password $svcPass | |
| Write-Status "Password updated for user: $svcUser" -Type Success | |
| } | |
| catch { | |
| Write-Status "Failed to update password: $_" -Type Error | |
| return $null | |
| } | |
| } | |
| else { | |
| # Create the service user | |
| try { | |
| New-LocalUser -Name $svcUser -Password $svcPass -FullName "Service Account - $svcUser" -Description "Service account for file sharing" -PasswordNeverExpires -UserMayNotChangePassword -ErrorAction Stop | Out-Null | |
| Write-Status "Created user: $svcUser" -Type Success | |
| } | |
| catch { | |
| Write-Status "Failed to create user: $_" -Type Error | |
| return $null | |
| } | |
| # Add user to Users group | |
| try { | |
| Add-LocalGroupMember -Group "Users" -Member $svcUser -ErrorAction Stop | |
| Write-Status "Added $svcUser to Users group" -Type Success | |
| } | |
| catch { | |
| Write-Status "Failed to add user to Users group: $_" -Type Error | |
| return $null | |
| } | |
| } | |
| return $svcUser | |
| } | |
| else { | |
| # Select existing user | |
| $localUsers = Get-LocalUser | Where-Object { $_.Enabled -eq $true } | Sort-Object Name | |
| Write-Host "`nExisting local users:" -ForegroundColor Cyan | |
| $userIndex = 1 | |
| $userMap = @{} | |
| foreach ($user in $localUsers) { | |
| Write-Host " [$userIndex] $($user.Name) - $($user.FullName)" -ForegroundColor White | |
| $userMap[$userIndex] = $user.Name | |
| $userIndex++ | |
| } | |
| $selection = Read-Host "`nSelect user number" | |
| if ($selection -and $userMap.ContainsKey([int]$selection)) { | |
| $selectedUser = $userMap[[int]$selection] | |
| Write-Status "Selected user: $selectedUser" -Type Success | |
| return $selectedUser | |
| } | |
| else { | |
| Write-Status "Invalid selection" -Type Error | |
| return $null | |
| } | |
| } | |
| } | |
| # Function to select drive | |
| function Select-Drive { | |
| # Get available drives | |
| $availableDrives = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Used -ne $null } | Sort-Object Name | |
| Write-Host "`nAvailable drives:" -ForegroundColor Cyan | |
| $driveIndex = 1 | |
| $driveMap = @{} | |
| foreach ($drive in $availableDrives) { | |
| $sizeGB = [math]::Round($drive.Used / 1GB + $drive.Free / 1GB, 2) | |
| $freeGB = [math]::Round($drive.Free / 1GB, 2) | |
| Write-Host " [$driveIndex] $($drive.Name):\ - Total: $sizeGB GB, Free: $freeGB GB" -ForegroundColor White | |
| $driveMap[$driveIndex] = $drive.Name + ':\' | |
| $driveIndex++ | |
| } | |
| # Let user select drive | |
| $selection = Read-Host "`nSelect drive number (or press Enter to type manually)" | |
| if ($selection -and $driveMap.ContainsKey([int]$selection)) { | |
| $shareRoot = $driveMap[[int]$selection] | |
| Write-Status "Selected: $shareRoot" -Type Success | |
| return $shareRoot | |
| } | |
| else { | |
| $shareRoot = Read-Host "Enter share root path (e.g., Z:\)" | |
| # Normalize the input - handle cases like "Z", "Z:", or "Z:\" | |
| $shareRoot = $shareRoot.Trim() | |
| # If just a drive letter, add colon and backslash | |
| if ($shareRoot -match '^[A-Za-z]$') { | |
| $shareRoot = $shareRoot + ':\' | |
| } | |
| # If drive letter with colon but no backslash, add backslash | |
| elseif ($shareRoot -match '^[A-Za-z]:$') { | |
| $shareRoot = $shareRoot + '\' | |
| } | |
| # If doesn't end with backslash, add it | |
| elseif (-not $shareRoot.EndsWith('\')) { | |
| $shareRoot = $shareRoot + '\' | |
| } | |
| Write-Status "Using path: $shareRoot" -Type Success | |
| return $shareRoot | |
| } | |
| } | |
| # Function to create share | |
| function New-FileShare { | |
| param( | |
| [string]$Username, | |
| [string]$ShareRoot, | |
| [string]$ShareDir, | |
| [string]$ComputerName | |
| ) | |
| $sharePath = Join-Path -Path $ShareRoot -ChildPath $ShareDir | |
| $shareName = $ShareDir | |
| $shareDescription = "Shared folder for $ShareDir" | |
| # Check if share path exists | |
| if (-not (Test-Path $sharePath)) { | |
| Write-Status "Share path does not exist: $sharePath" -Type Error | |
| $create = Read-Host "Would you like to create it? (Y/N)" | |
| if ($create -eq 'Y') { | |
| try { | |
| New-Item -Path $sharePath -ItemType Directory -Force | Out-Null | |
| Write-Status "Created directory: $sharePath" -Type Success | |
| } | |
| catch { | |
| Write-Status "Failed to create directory: $_" -Type Error | |
| return $false | |
| } | |
| } | |
| else { | |
| Write-Status "Aborting - share path does not exist" -Type Error | |
| return $false | |
| } | |
| } | |
| # Validate user exists before applying permissions | |
| $userExists = Get-LocalUser -Name $Username -ErrorAction SilentlyContinue | |
| if (-not $userExists) { | |
| Write-Status "User '$Username' does not exist. Cannot set permissions." -Type Error | |
| return $false | |
| } | |
| # Set NTFS permissions on the share path | |
| try { | |
| # Enable inheritance first | |
| $inheritResult = icacls "$sharePath" /inheritance:e 2>&1 | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Status "Warning: Failed to enable inheritance - $inheritResult" -Type Warning | |
| } | |
| # Grant permissions using fully qualified username (COMPUTERNAME\Username) | |
| $icaclsResult = icacls "$sharePath" /grant "${ComputerName}\${Username}:(OI)(CI)M" /t 2>&1 | |
| if ($LASTEXITCODE -eq 0) { | |
| Write-Status "Set NTFS permissions for ${ComputerName}\${Username} on $sharePath" -Type Success | |
| } | |
| else { | |
| Write-Status "Warning: icacls returned exit code $LASTEXITCODE - $icaclsResult" -Type Warning | |
| } | |
| } | |
| catch { | |
| Write-Status "Failed to set NTFS permissions: $_" -Type Error | |
| return $false | |
| } | |
| # Check if SMB share already exists | |
| $existingShare = Get-SmbShare -Name $shareName -ErrorAction SilentlyContinue | |
| if ($existingShare) { | |
| Write-Status "SMB Share '$shareName' already exists" -Type Warning | |
| $removeShare = Read-Host "Would you like to remove and recreate it? (Y/N)" | |
| if ($removeShare -eq 'Y') { | |
| try { | |
| Remove-SmbShare -Name $shareName -Force | |
| Write-Status "Removed existing share: $shareName" -Type Success | |
| } | |
| catch { | |
| Write-Status "Failed to remove existing share: $_" -Type Error | |
| return $false | |
| } | |
| } | |
| else { | |
| Write-Status "Skipping share creation - using existing share" -Type Info | |
| # Update permissions on existing share | |
| try { | |
| Grant-SmbShareAccess -Name $shareName -AccountName "${ComputerName}\${Username}" -AccessRight Full -Force | Out-Null | |
| Write-Status "Updated permissions on existing share" -Type Success | |
| } | |
| catch { | |
| Write-Status "Failed to update share permissions: $_" -Type Error | |
| } | |
| return $true | |
| } | |
| } | |
| # Create new SMB share | |
| try { | |
| New-SmbShare -Name $shareName -Path $sharePath -FullAccess "${ComputerName}\${Username}" -Description $shareDescription -ErrorAction Stop | Out-Null | |
| Write-Status "Created SMB share: \\$ComputerName\$shareName" -Type Success | |
| } | |
| catch { | |
| Write-Status "Failed to create SMB share: $_" -Type Error | |
| return $false | |
| } | |
| # Display share info | |
| Write-Host "" | |
| Write-Status "Share created successfully!" -Type Success | |
| Write-Host " Share Name: $shareName" -ForegroundColor White | |
| Write-Host " Share Path: $sharePath" -ForegroundColor White | |
| Write-Host " UNC Path: \\$ComputerName\$shareName" -ForegroundColor White | |
| Write-Host " Permissions: Full Control for $Username" -ForegroundColor White | |
| Write-Host "" | |
| return $true | |
| } | |
| # Main script execution | |
| $srvName = $env:COMPUTERNAME | |
| Write-Status "File Server Management Script" -Type Info | |
| Write-Status "Computer Name: $srvName" -Type Info | |
| # Enable File and Printer Sharing firewall rules once at startup | |
| try { | |
| Set-NetFirewallRule -Group "File and Printer Sharing" -Profile Domain,Private -Enabled True -ErrorAction Stop | |
| Write-Status "Firewall rules enabled for File and Printer Sharing (Domain, Private)" -Type Success | |
| } | |
| catch { | |
| Write-Status "Failed to enable firewall rules: $_" -Type Warning | |
| } | |
| while ($true) { | |
| $choice = Show-MainMenu | |
| switch ($choice) { | |
| "1" { | |
| # Create new service user only | |
| Write-Host "" | |
| Write-Status "Creating new service user..." -Type Info | |
| $svcUser = Get-OrCreateUser -CreateNew $true | |
| if (-not $svcUser) { | |
| Write-Status "Failed to create/configure user. Returning to menu." -Type Error | |
| continue | |
| } | |
| Write-Host "" | |
| Write-Status "========================================" -Type Success | |
| Write-Status "User created successfully!" -Type Success | |
| Write-Status "========================================" -Type Success | |
| Write-Host "" | |
| Write-Host " Username: $svcUser" -ForegroundColor White | |
| Write-Host " Computer: $srvName" -ForegroundColor White | |
| Write-Host "" | |
| Write-Status "Next step: Use option [2] to add shares for this user" -Type Info | |
| Write-Host "" | |
| } | |
| "2" { | |
| # Add share to any user (new or existing) | |
| Write-Host "" | |
| Write-Status "Add share to user..." -Type Info | |
| Write-Host "" | |
| Write-Host "Would you like to:" -ForegroundColor Cyan | |
| Write-Host " [1] Select existing user" -ForegroundColor White | |
| Write-Host " [2] Create new user first" -ForegroundColor White | |
| Write-Host "" | |
| $userChoice = Read-Host "Select option (1 or 2)" | |
| if ($userChoice -eq "2") { | |
| $svcUser = Get-OrCreateUser -CreateNew $true | |
| } else { | |
| $svcUser = Get-OrCreateUser -CreateNew $false | |
| } | |
| if (-not $svcUser) { | |
| Write-Status "No user selected. Returning to menu." -Type Error | |
| continue | |
| } | |
| $shareRoot = Select-Drive | |
| $shareDir = Read-Host "Enter share directory name (e.g., Keystone)" | |
| $result = New-FileShare -Username $svcUser -ShareRoot $shareRoot -ShareDir $shareDir -ComputerName $srvName | |
| if (-not $result) { | |
| Write-Status "Failed to create share" -Type Error | |
| } | |
| } | |
| "3" { | |
| # List existing users | |
| Write-Host "" | |
| Write-Status "Listing local users..." -Type Info | |
| $localUsers = Get-LocalUser | Sort-Object Name | |
| foreach ($user in $localUsers) { | |
| $status = if ($user.Enabled) { "Enabled" } else { "Disabled" } | |
| $statusColor = if ($user.Enabled) { "Green" } else { "Red" } | |
| Write-Host " $($user.Name) - " -NoNewline -ForegroundColor White | |
| Write-Host "$status" -ForegroundColor $statusColor | |
| if ($user.Description) { | |
| Write-Host " Description: $($user.Description)" -ForegroundColor Gray | |
| } | |
| } | |
| } | |
| "4" { | |
| # List existing shares | |
| Write-Host "" | |
| Write-Status "Listing SMB shares..." -Type Info | |
| $shares = Get-SmbShare | Where-Object { $_.Special -eq $false } | Sort-Object Name | |
| foreach ($share in $shares) { | |
| Write-Host " $($share.Name)" -ForegroundColor White | |
| Write-Host " Path: $($share.Path)" -ForegroundColor Gray | |
| Write-Host " UNC: \\$srvName\$($share.Name)" -ForegroundColor Gray | |
| if ($share.Description) { | |
| Write-Host " Description: $($share.Description)" -ForegroundColor Gray | |
| } | |
| Write-Host "" | |
| } | |
| } | |
| "5" { | |
| # Exit | |
| Write-Status "Exiting..." -Type Info | |
| exit 0 | |
| } | |
| default { | |
| Write-Status "Invalid selection. Please choose 1-5." -Type Error | |
| } | |
| } | |
| # Pause before showing menu again | |
| Write-Host "" | |
| Read-Host "Press Enter to continue" | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment