Public/New-FSMTargetShares.ps1

function New-FSMTargetShares {
    <#
    .SYNOPSIS
        Creates folders and SMB shares on a target file server from an exported CSV.

    .DESCRIPTION
        Reads a share CSV and, for each entry, creates the backing folder (if missing)
        and the SMB share on the target server, carrying over description, enumeration
        mode, caching, continuous availability and encryption where present.

        SAFETY: this runs in preview mode by default and changes nothing. Add -Execute
        to actually create folders and shares. Each share is handled independently in a
        try/catch, so one failure produces a 'Failed' result row instead of aborting the
        whole batch.

    .PARAMETER TargetServer
        The file server to create shares on.

    .PARAMETER ShareCsvPath
        Path to the share inventory CSV (optionally already remapped).

    .PARAMETER DefaultFullAccess
        Initial share-level Full Access principal(s) applied at creation. Real
        permissions are applied separately by Grant-FSMTargetSharePermissions; use
        -RemoveEveryone there to strip the default afterwards. Defaults to 'Everyone'.

    .PARAMETER ScopeName
        Optional. For clustered (scale-out / classic) file server roles, the scope name
        the share belongs to. Leave blank for a standalone server.

    .PARAMETER Credential
        Optional credentials for the remote connection.

    .PARAMETER Execute
        Actually create folders and shares. Without it, you get a preview only.

    .EXAMPLE
        New-FSMTargetShares -TargetServer newfs01 -ShareCsvPath C:\Temp\remapped.csv

    .EXAMPLE
        New-FSMTargetShares -TargetServer newfs01 -ShareCsvPath C:\Temp\remapped.csv -Execute
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '',
        Justification = 'Uses an explicit -Execute switch instead of ShouldProcess/-WhatIf, because ShouldProcess does not propagate into the remote Invoke-Command scriptblock where the change actually happens. Preview is the default; nothing changes without -Execute.')]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$TargetServer,

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ShareCsvPath,

        [string[]]$DefaultFullAccess = @('Everyone'),

        [string]$ScopeName,

        [pscredential]$Credential,

        [switch]$Execute
    )

    if (-not (Test-Path -Path $ShareCsvPath)) {
        throw "Share CSV not found: '$ShareCsvPath'."
    }

    $shares = @(Import-Csv -Path $ShareCsvPath)
    Assert-FSMCsvColumn -InputObject $shares -RequiredColumn 'Name', 'Path' -Path $ShareCsvPath

    Invoke-FSMRemote -ComputerName $TargetServer -Credential $Credential `
        -ArgumentList $shares, $DefaultFullAccess, $ScopeName, [bool]$Execute -ScriptBlock {
        param($Shares, [string[]]$DefaultFullAccess, [string]$ScopeName, [bool]$Execute)

        # Local, robust string-to-bool used for CSV round-tripped values.
        $toBool = {
            param($value)
            if ($null -eq $value) { return $false }
            if ($value -is [bool]) { return $value }
            $text = "$value".Trim()
            if ($text -in @('1', 'true', 'yes', 'y', 'on'))  { return $true }
            if ($text -in @('0', 'false', 'no', 'n', 'off', '')) { return $false }
            return [bool]$text
        }

        foreach ($share in $Shares) {
            try {
                $existingShare = Get-SmbShare -Name $share.Name -ErrorAction SilentlyContinue
                if ($existingShare) {
                    [pscustomobject]@{
                        ShareName = $share.Name; Path = $existingShare.Path
                        Action = 'Skipped'; Reason = 'Share already exists'
                    }
                    continue
                }

                if (-not $Execute) {
                    [pscustomobject]@{
                        ShareName = $share.Name; Path = $share.Path
                        Action = 'WhatIf'; Reason = 'Would create folder and share. Re-run with -Execute to apply.'
                    }
                    continue
                }

                if (-not (Test-Path -Path $share.Path)) {
                    New-Item -Path $share.Path -ItemType Directory -Force -ErrorAction Stop | Out-Null
                }

                $newShareParams = @{
                    Name        = $share.Name
                    Path        = $share.Path
                    FullAccess  = $DefaultFullAccess
                    ErrorAction = 'Stop'
                }
                if ($ScopeName)                                                      { $newShareParams.ScopeName = $ScopeName }
                if ($share.PSObject.Properties.Name -contains 'Description' -and $share.Description)                     { $newShareParams.Description = $share.Description }
                if ($share.PSObject.Properties.Name -contains 'FolderEnumerationMode' -and $share.FolderEnumerationMode) { $newShareParams.FolderEnumerationMode = $share.FolderEnumerationMode }
                if ($share.PSObject.Properties.Name -contains 'CachingMode' -and $share.CachingMode)                     { $newShareParams.CachingMode = $share.CachingMode }
                if ($share.PSObject.Properties.Name -contains 'ContinuouslyAvailable') { $newShareParams.ContinuouslyAvailable = (& $toBool $share.ContinuouslyAvailable) }
                if ($share.PSObject.Properties.Name -contains 'EncryptData')           { $newShareParams.EncryptData = (& $toBool $share.EncryptData) }

                New-SmbShare @newShareParams | Out-Null

                [pscustomobject]@{
                    ShareName = $share.Name; Path = $share.Path
                    Action = 'Created'; Reason = 'Share created successfully'
                }
            }
            catch {
                [pscustomobject]@{
                    ShareName = $share.Name
                    Path      = $share.Path
                    Action    = 'Failed'
                    Reason    = $_.Exception.Message
                }
            }
        }
    }
}