Skip to content

Instantly share code, notes, and snippets.

@bitmvr
Created October 27, 2025 16:23
Show Gist options
  • Select an option

  • Save bitmvr/691288606fde9b5de20f9f1f632de937 to your computer and use it in GitHub Desktop.

Select an option

Save bitmvr/691288606fde9b5de20f9f1f632de937 to your computer and use it in GitHub Desktop.
Interactive File Share Manager for Windows
# 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