Introduction

If you've ever constantly been at the screen while setting up shared folders and permissions manually, we've got just the thing for you: a nifty PowerShell script. Forget about digging through settings and risking typos – this script does the heavy lifting in a flash.

Script Explanation

I will never recommend anybody copy and paste into your terminal without knowing what the script is doing, be sure to look over the script before running it.

As you can see, in the script most of what I already commented on it, and stated it what is doing. Here is the script in a nutshell:

Launching this script in a non-admin environment will escalate to the admin prompt. After that, in case any of the commands need to be checked later, I save the comment execution log file to the home directory of the current login user, the file is named 'creating-user-share_LOGs.txt', if the logs file already exists, it will detect the number of files with the same name and add the number after it( If the script runs multiple time).

The next section is the function for user input validation, to enforce limitations on user input for folder paths, usernames, full names, passwords, and share names, it uses some regular expressions to validate the input, and the script will display informative messages and warnings if the input does not meet the requirements specified for each variable (This not an extensive limitation, but covers the basic, such as this cannot include special characters in some folder, or the password must be a minimum of eight, etc...). It will then prompt the user to enter the input again. It is a conditional statement to prompt the user for custom input and provide default values if no input is provided. However, if the user does not want to enter custom values to replace the default variable, the user hits Enter without typing anything, and it will default to the predetermined values specified in the script ( to improve the flexibility of the script and user experience, this is something I added in later). Default values are: create a folder called "Scans-Folder" under the user home directory with the following default:

$userNameDefault = "scansonly"
$fullNameDefault = "Scans Only"
$passwordDefault = "P@ssw0rd"
$shareNameDefault = "ScansShare"

As you go down to the script, the next section is the function to create a user function to do shared and the function to create a folder and its permissions, ensure that the folder belongs to the user recursively under every subdirectory, add checks before creating each item (folder, share, and user) to ensure they don't already exist. If they do exist, we'll skip the creation and provide a message indicating that the item already exists.

Before this step, nothing had been done to your system. We are doing all that just to prepare to get to the next five steps, all the preparation was the environment variable for the following setup; At stop 0, set up the firewall rules, it will search the firewall rules by looking for "*File and Printer Sharing*" (Different OS, there might be a discrepancy in the name) and set them to any subnet, check for firewall rules action is set to allow, and enable it, in addition, the script also looking for the network connection profile status, if the current network connection is something different, it will change it to "private". Then steps 1-5 will follow the previously defined variable, the script will create the user, then create the folder with permissions, followed by creating the share, and finally grant permissions to the user on the share.

Finally, at the end of the script print out the relevant information after the successful creation of the share and username, IP address, Share Path, etc for easy copy, the success message, and the prompt for the user to press "q" to exit PowerShell if all steps were successful. At this point, the script will stop and the log file also will be closed.

The PowerShell Script

Not guaranteed to be compatible with every single version of windows, tested on most recent Windows 10/11 Pro

Run the following code as the administrator in PowerShell:

Link 1:

Set-ExecutionPolicy Bypass -Scope Process -Force; irm https://techssh.com/Files-share/Folder-shared.ps1 | iex

Link 2: Windows 11

Set-ExecutionPolicy Bypass -Scope Process -Force; irm raw.githubusercontent.com/Techssh/Handy-Scripts/main/PowerShell/A-PowerShell-Script-for-Creating-Shared-Folders-and-its-Permission.ps1 | iex

How to execute the command manually: Either copy and paste the following script into any Notepad and save it as .ps1 file, or you can download the script from my GitHub page, or my site with the .ps1 or .txt file extension option.

Detail shell script:

####Note####
#Open PowerShell as an Administrator
#Type: Set-ExecutionPolicy Unrestricted
#Once the entire script has finished running, type: Set-ExecutionPolicy Restricted

##Tested on Windows 10/11###

#This is based on whatever window SMB version is running, if you require SMBv1 (not recommended), be sure to enable it


###running this script that save in networks share may not work###
#make sure the script is running with full admin privileges
#### START ELEVATE TO ADMIN #####
param(
    [Parameter(Mandatory=$false)]
    [switch]$shouldAssumeToBeElevated,

    [Parameter(Mandatory=$false)]
    [String]$workingDirOverride
)

# If parameter is not set, we are propably in non-admin execution. We set it to the current working directory so that
#  the working directory of the elevated execution of this script is the current working directory
if(-not($PSBoundParameters.ContainsKey('workingDirOverride')))
{
    $workingDirOverride = (Get-Location).Path
}

function Test-Admin {
    $currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
    $currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}

# If we are in a non-admin execution. Execute this script as admin
if ((Test-Admin) -eq $false)  {
    if ($shouldAssumeToBeElevated) {
        Write-Output "Elevating did not work :("

    } else {
        #                                                         vvvvv add `-noexit` here for better debugging vvvvv 
        Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -shouldAssumeToBeElevated -workingDirOverride "{1}"' -f ($myinvocation.MyCommand.Definition, "$workingDirOverride"))
    }
    exit
}

Set-Location "$workingDirOverride"
##### END ELEVATE TO ADMIN #####

# Add actual commands to be executed in elevated mode here:
Write-Output "Admin in PowerShell elevation successful"


###-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------###






# Check for administrative privileges
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
    Write-Host "Please run this script with administrative privileges."
    Write-Host "Press any key to exit..."
    $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
    exit
}

# Set the log file path
$logFileBaseName = "creating-user-share_LOGs"
$logFileExtension = ".txt"
$logFileIndex = 0
$logFilePath = Join-Path -Path $Env:USERPROFILE -ChildPath ($logFileBaseName + $logFileExtension)

# Check if log file exists, if yes, create additional log files with sequential numbers
while (Test-Path $logFilePath) {
    $logFileIndex++
    $logFilePath = Join-Path -Path $Env:USERPROFILE -ChildPath ($logFileBaseName + "_$logFileIndex" + $logFileExtension)
}

# Redirect all output to the log file
Start-Transcript -Path $logFilePath


###-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------###

## Default values, not USE!!!
#$folderPath = Join-Path -Path $Env:USERPROFILE -ChildPath "Scans-Folder"
#$userName = "scansonly"
#$fullName = "Scans Only"
#$password = "P@ssw0rd"
#$shareName = "Scans-Share"


# Function to validate folder path
function Validate-FolderPath {
    param([string]$folderPath)
    if (-not ($folderPath -match "^([A-Za-z]:\\[^/:*?""<>|]+)$")) {
        Write-Warning "Invalid folder path. Folder path must be a valid Windows path (e.g., C:\folder)."
        return $false
    }
    return $true
}

# Function to validate username
function Validate-Username {
    param([string]$username)
    if (-not ($username -match "^[a-zA-Z0-9_]+$")) {
        Write-Warning "Invalid username. Username must contain only letters, numbers, and underscores."
        return $false
    }
    return $true
}

# Function to validate full name
function Validate-FullName {
    param([string]$fullName)
    if (-not ($fullName -match "^[\w\s'-]+$")) {
        Write-Warning "Invalid full name. Full name must contain only letters, numbers, spaces, hyphens, and apostrophes."
        return $false
    }
    return $true
}




# Function to validate password
function Validate-Password {
    param([string]$password)
    if (-not ([string]::IsNullOrEmpty($password) -or $password.Length -ge 8)) {
        Write-Warning "Invalid password. Password must be at least eight characters long."
        return $false
    }
    return $true
}




# Function to validate share name
function Validate-ShareName {
    param([string]$shareName)
    if (-not ($shareName -match "^[a-zA-Z0-9_]+$")) {
        Write-Warning "Invalid share name. Share name must contain only letters, numbers, and underscores; your computer policy may require you to have a more complex password too."
        return $false
    }
    return $true
}

# Default values prompting for custom input
$folderPathDefault = Join-Path -Path $Env:USERPROFILE -ChildPath "Scans-Folder"
$userNameDefault = "scansonly"
$fullNameDefault = "Scans Only"
$passwordDefault = "P@ssw0rd"
$shareNameDefault = "ScansShare"

do {
    $folderPathInput = Read-Host "Enter folder path (default: $folderPathDefault)"
    if ([string]::IsNullOrEmpty($folderPathInput)) {
        $folderPath = $folderPathDefault
        break
    }
} until (Validate-FolderPath $folderPathInput)

if (-not [string]::IsNullOrEmpty($folderPathInput)) {
    $folderPath = $folderPathInput
}

do {
    $userNameInput = Read-Host "Enter username (default: $userNameDefault)"
    if ([string]::IsNullOrEmpty($userNameInput)) {
        $userName = $userNameDefault
        break
    }
} until (Validate-Username $userNameInput)

if (-not [string]::IsNullOrEmpty($userNameInput)) {
    $userName = $userNameInput
}

do {
    $fullNameInput = Read-Host "Enter full name (default: $fullNameDefault)"
    if ([string]::IsNullOrEmpty($fullNameInput)) {
        $fullName = $fullNameDefault
        break
    }
} until (Validate-FullName $fullNameInput)

if (-not [string]::IsNullOrEmpty($fullNameInput)) {
    $fullName = $fullNameInput
}





do {
    $passwordInput = Read-Host "Enter password (default: $passwordDefault)"
    if ([string]::IsNullOrEmpty($passwordInput)) {
        $password = $passwordDefault
        break
    }
} until (Validate-Password $passwordInput)

if (-not [string]::IsNullOrEmpty($passwordInput)) {
    $password = $passwordInput
}



do {
    $shareNameInput = Read-Host "Enter share name (default: $shareNameDefault)"
    if ([string]::IsNullOrEmpty($shareNameInput)) {
        $shareName = $shareNameDefault
        break
    }
} until (Validate-ShareName $shareNameInput)

if (-not [string]::IsNullOrEmpty($shareNameInput)) {
    $shareName = $shareNameInput
}

# Displaying info message
Write-Host "Congratulations! You've successfully completed all inputs."
Write-Host "Folder Path: $folderPath"
Write-Host "Username: $userName"
Write-Host "Full Name: $fullName"
Write-Host "Password: $password"
Write-Host "Share Name: $shareName"




###-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------###


# Function to create user
function Create-LocalUser {
    param(
        [string]$userName,
        [string]$password,
        [string]$fullName
    )

    $userExists = $false
    $user = Get-LocalUser -Name $userName -ErrorAction SilentlyContinue
    if ($user) {
        Write-Host "User '$userName' already exists."
        $userExists = $true
    } else {
        $securePassword = ConvertTo-SecureString $password -AsPlainText -Force
        $userParams = @{
            Name = $userName
            Password = $securePassword
            FullName = $fullName
            PasswordNeverExpires = $true
            UserMayNotChangePassword = $true
        }
        New-LocalUser @userParams
        Write-Host "User '$userName' created."
    }
    return $userExists
}

# Function to create share
function Create-Share {
    param(
        [string]$shareName,
        [string]$folderPath
    )

    if (-not (Get-SmbShare | Where-Object { $_.Name -eq $shareName })) {
        New-SmbShare -Name $shareName -Path $folderPath -FullAccess "Everyone"
        Write-Host "Share '$shareName' created."
    } else {
        Write-Host "Share '$shareName' already exists."
    }
}

# Function to create folder and set permissions recursively
function Create-Folder {
    param(
        [string]$folderPath,
        [string]$userName
    )

    if (-not (Test-Path $folderPath -PathType Container)) {
        New-Item -ItemType Directory -Path $folderPath
        Write-Host "Folder '$folderPath' created."
    } else {
        Write-Host "Folder '$folderPath' already exists."
    }

    if (-not [string]::IsNullOrEmpty($userName)) {
        $acl = Get-Acl $folderPath
        $rule = New-Object System.Security.AccessControl.FileSystemAccessRule("$userName", "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow")
        $acl.AddAccessRule($rule)
        Set-Acl $folderPath $acl
        Write-Host "Permissions granted to user '$userName' on folder '$folderPath' and propagated to all child items."
    } else {
        Write-Host "User name is null or empty. No permissions were granted."
    }
}

# Function to grant permissions to share
function Grant-Permissions {
    param(
        [string]$userName,
        [string]$shareName
    )

    $share = Get-SmbShare | Where-Object { $_.Name -eq $shareName }
    if ($share) {
        Grant-SmbShareAccess -Name $shareName -AccountName $userName -AccessRight Full
        Write-Host "Permissions granted to user '$userName' on share '$shareName'."
    } else {
        Write-Host "Share '$shareName' not found. Permissions not granted."
    }
}

# Step 0: Enable file and printer sharing firewall rules
$firewallRule = Get-NetFirewallRule | Where-Object { $_.DisplayName -like "*File and Printer Sharing*" }
if ($firewallRule -ne $null) {
    # Ensure rule is in the "Allow" connection state
    if ($firewallRule.Action -ne "Allow") {
        Set-NetFirewallRule -Name $firewallRule.Name -Action Allow
    }
    # Allow connections from any remote subnet
    Set-NetFirewallRule -Name $firewallRule.Name -RemoteAddress Any
    # Enable the rule if it's not already enabled
    if (-not $firewallRule.Enabled) {
        Set-NetFirewallRule -Name $firewallRule.Name -Enabled True
    }
} else {
    Write-Host "File and Printer Sharing firewall rule not found."
}



Write-Host "Your Wi-Fi or Ethernet has a connection (LAN connection it's good enough) to complete it successfully for the following step 1! Otherwise, having an error is normal behavior."
# Step 1: Check network profile and set if needed
$networkProfile = Get-NetConnectionProfile
if ($networkProfile.NetworkCategory -ne "Private") {
    Set-NetConnectionProfile -Name $networkProfile.Name -NetworkCategory Private
}

# Step 2: Create user
$userExists = Create-LocalUser -userName $userName -password $password -fullName $fullName

# Step 3: Create folder
Create-Folder -folderPath $folderPath -userName $userName

# Step 4: Create share
Create-Share -shareName $shareName -folderPath $folderPath

# Step 5: Grant permissions
if (-not $userExists) {
    Grant-Permissions -userName $userName -shareName $shareName
}

# Print share information if all steps were successful
if ((Test-Path $folderPath) -and (Get-SmbShare | Where-Object { $_.Name -eq $shareName }) -and (Get-LocalUser | Where-Object { $_.Name -eq $userName })) {
    $hostname = hostname
    $ipv4 = (Get-NetIPAddress -AddressFamily IPv4).IPAddress
    $ipv6 = (Get-NetIPAddress -AddressFamily IPv6).IPAddress

    Write-Host "All steps completed successfully!"
    Write-Host "---------------------------------------o(* ̄▽ ̄*)ブ"
    Write-Host "Share Information:"
    Write-Host "  Hostname: $hostname"
    Write-Host "  IPv4 Addresses: $ipv4"
    Write-Host "  IPv6 Addresses: $ipv6"
    Write-Host "  Share Name: $shareName"
    Write-Host "  Share Path: \\$hostname\$shareName"
    Write-Host "  Username: $userName"
    Write-Host "  Password: $password" # Displaying password for convenience, make sure to change this in a production environment
    Write-Host "完---------------------------------------o(* ̄▽ ̄*)ブ"

    Write-Host "Press 'q' to exit PowerShell..."
    $key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
    if ($key.Character -eq "q") {
        exit
    }
} else {
    Write-Host "An error occurred during the process. Please check and try again."
}

# End transcript and close log file
Stop-Transcript

If you want to undo all those settings, just change the firewall rules and the network profile you have to change back manually, delete the folder you created, and delete the user you created, after those actions, the share also deletes automatically.

Recommendation

This script is intended for Windows local PCs, not for the domain environment, but you can always customize and suit your needs. Please let me know if the scripts do not work in your case, and comment below if you have any suggestions or want to improve the script.