DscResources/MSFT_xSmbShare/MSFT_xSmbShare.psm1

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [parameter(Mandatory = $true)]
        [System.String]
        $Path
    )

    $smbShare = Get-SmbShare -Name $Name -ErrorAction SilentlyContinue
    $changeAccess = @()
    $readAccess = @()
    $fullAccess = @()
    $noAccess = @()
    if ($smbShare -ne $null)
    {
        $smbShareAccess = Get-SmbShareAccess -Name $Name
        $smbShareAccess | ForEach-Object  {
            $access = $_;
            if ($access.AccessRight -eq 'Change' -and $access.AccessControlType -eq 'Allow')
            {
                $changeAccess += $access.AccountName
            }
            elseif ($access.AccessRight -eq 'Read' -and $access.AccessControlType -eq 'Allow')
            {
                $readAccess += $access.AccountName
            }
            elseif ($access.AccessRight -eq 'Full' -and $access.AccessControlType -eq 'Allow')
            {
                $fullAccess += $access.AccountName
            }
            elseif ($access.AccessRight -eq 'Full' -and $access.AccessControlType -eq 'Deny')
            {
                $noAccess += $access.AccountName
            }
        }
    }
    else
    {
        Write-Verbose "Share with name $Name does not exist"
    }

    $returnValue = @{
        Name                  = $smbShare.Name
        Path                  = $smbShare.Path
        Description           = $smbShare.Description
        ConcurrentUserLimit   = $smbShare.ConcurrentUserLimit
        EncryptData           = $smbShare.EncryptData
        FolderEnumerationMode = $smbShare.FolderEnumerationMode
        CachingMode           = $smbShare.CachingMode
        ShareState            = $smbShare.ShareState
        ShareType             = $smbShare.ShareType
        ShadowCopy            = $smbShare.ShadowCopy
        Special               = $smbShare.Special
        ChangeAccess          = $changeAccess
        ReadAccess            = $readAccess
        FullAccess            = $fullAccess
        NoAccess              = $noAccess
        Ensure                = if($smbShare) {"Present"} else {"Absent"}
    }

    $returnValue
}

function Set-AccessPermission
{
    [CmdletBinding()]
    Param
    (
        $ShareName,

        [string[]]
        $UserName,

        [string]
        [ValidateSet("Change","Full","Read","No")]
        $AccessPermission
    )
    $formattedString = '{0}{1}' -f $AccessPermission,"Access"
    Write-Verbose -Message "Setting $formattedString for $UserName"

    if ($AccessPermission -eq "Change" -or $AccessPermission -eq "Read" -or $AccessPermission -eq "Full")
    {
        Grant-SmbShareAccess -Name $Name -AccountName $UserName -AccessRight $AccessPermission -Force
    }
    else
    {
        Block-SmbShareAccess -Name $Name -AccountName $userName -Force
    }
}

Function Set-BoundParameters
{
    # Define parameters
    Param
    (
        $BoundParameters
    )

    # Check for null access before passing to New-SmbShare
    if (($BoundParameters.ContainsKey("ChangeAccess")) -and ([string]::IsNullOrEmpty($BoundParameters["ChangeAccess"])))
    {
        Write-Verbose "Parameter ChangeAccess is null or empty, removing from collection."
        # Remove the parameter
        $BoundParameters.Remove("ChangeAccess")
    }

    if (($BoundParameters.ContainsKey("ReadAccess")) -and ([string]::IsNullOrEmpty($BoundParameters["ReadAccess"])))
    {
        Write-Verbose "Paramater ReadAccess is null or empty, removing from collection."
        # Remove the parameter
        $BoundParameters.Remove("ReadAccess")
    }

    if (($BoundParameters.ContainsKey("FullAccess")) -and ([string]::IsNullOrEmpty($BoundParameters["FullAccess"])))
    {
        Write-Verbose "Parameter FullAccess is null or empty, removing from collection."
        # Remove the parameter
        $BoundParameters.Remove("FullAccess")
    }

    if (($BoundParameters.ContainsKey("NoAccess")) -and ([string]::IsNullOrEmpty($BoundParameters["NoAccess"])))
    {
        Write-Verbose "Parameter NoAccess is null or empty, removing from collection."
        # Remove the parameter
        $BoundParameters.Remove("NoAccess")
    }

    # Return the parameter collection
    return $BoundParameters
}

function Remove-AccessPermission
{
    [CmdletBinding()]
    Param
    (
        $ShareName,

        [string[]]
        $UserName,

        [string]
        [ValidateSet("Change","Full","Read","No")]
        $AccessPermission
    )
    $formattedString = '{0}{1}' -f $AccessPermission,"Access"
    Write-Debug -Message "Removing $formattedString for $UserName"

    if ($AccessPermission -eq "Change" -or $AccessPermission -eq "Read" -or $AccessPermission -eq "Full")
    {
        Revoke-SmbShareAccess -Name $Name -AccountName $UserName -Force

}
    else
    {
        UnBlock-SmbShareAccess -Name $Name -AccountName $userName -Force
    }
}

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [parameter(Mandatory = $true)]
        [System.String]
        $Path,

        [System.String]
        $Description,

        [System.String[]]
        $ChangeAccess,

        [System.UInt32]
        $ConcurrentUserLimit,

        [System.Boolean]
        $EncryptData,

        [ValidateSet("AccessBased","Unrestricted")]
        [System.String]
        $FolderEnumerationMode,

        [ValidateSet("None","Manual","Programs","Documents","BranchCache")]
        [System.String]
        $CachingMode,

        [System.String[]]
        $FullAccess,

        [System.String[]]
        $NoAccess,

        [System.String[]]
        $ReadAccess,

        [ValidateSet("Present","Absent")]
        [System.String]
        $Ensure = 'Present'
    )

    $PSBoundParameters.Remove("Debug")

    $shareExists = $false
    $smbShare = Get-SmbShare -Name $Name -ErrorAction SilentlyContinue
    if($smbShare -ne $null)
    {
        Write-Verbose -Message "Share with name $Name exists"
        $shareExists = $true
    }
    if ($Ensure -eq "Present")
    {
        if ($shareExists -eq $false)
        {
            $PSBoundParameters.Remove("Ensure")
            Write-Verbose "Creating share $Name to ensure it is Present"

            # Alter bound parameters
            $newShareParameters = Set-BoundParameters -BoundParameters $PSBoundParameters

            # Pass the parameter collection to New-SmbShare
            New-SmbShare @newShareParameters
        }
        else
        {
            # Need to call either Set-SmbShare or *ShareAccess cmdlets
            if ($PSBoundParameters.ContainsKey("ChangeAccess"))
            {
                $changeAccessValue = $PSBoundParameters["ChangeAccess"]
                $PSBoundParameters.Remove("ChangeAccess")
            }
            if ($PSBoundParameters.ContainsKey("ReadAccess"))
            {
                $readAccessValue = $PSBoundParameters["ReadAccess"]
                $PSBoundParameters.Remove("ReadAccess")
            }
            if ($PSBoundParameters.ContainsKey("FullAccess"))
            {
                $fullAccessValue = $PSBoundParameters["FullAccess"]
                $PSBoundParameters.Remove("FullAccess")
            }
            if ($PSBoundParameters.ContainsKey("NoAccess"))
            {
                $noAccessValue = $PSBoundParameters["NoAccess"]
                $PSBoundParameters.Remove("NoAccess")
            }

            # Use Set-SmbShare for performing operations other than changing access
            $PSBoundParameters.Remove("Ensure")
            $PSBoundParameters.Remove("Path")
            Set-SmbShare @PSBoundParameters -Force

            # Use *SmbShareAccess cmdlets to change access
            $smbShareAccessValues = Get-SmbShareAccess -Name $Name

            # Remove Change permissions
            $smbShareAccessValues | Where-Object {$_.AccessControlType  -eq 'Allow' -and $_.AccessRight -eq 'Change'} `
                                    | ForEach-Object {
                                        Remove-AccessPermission -ShareName $Name -UserName $_.AccountName -AccessPermission Change
                                        }

            if ($ChangeAccess -ne $null)
            {
                # Add change permissions
                $changeAccessValue | ForEach-Object {
                                        Set-AccessPermission -ShareName $Name -AccessPermission "Change" -Username $_
                                       }
            }

            $smbShareAccessValues = Get-SmbShareAccess -Name $Name

            # Remove read access
            $smbShareAccessValues | Where-Object {$_.AccessControlType  -eq 'Allow' -and $_.AccessRight -eq 'Read'} `
                                    | ForEach-Object {
                                        Remove-AccessPermission -ShareName $Name -UserName $_.AccountName -AccessPermission Read
                                        }

            if ($ReadAccess -ne $null)
            {
                # Add read access
                $readAccessValue | ForEach-Object {
                                       Set-AccessPermission -ShareName $Name -AccessPermission "Read" -Username $_
                                     }
            }


            $smbShareAccessValues = Get-SmbShareAccess -Name $Name

            # Remove full access
            $smbShareAccessValues | Where-Object {$_.AccessControlType  -eq 'Allow' -and $_.AccessRight -eq 'Full'} `
                                    | ForEach-Object {
                                        Remove-AccessPermission -ShareName $Name -UserName $_.AccountName -AccessPermission Full
                                        }


            if ($FullAccess -ne $null)
            {

                # Add full access
                $fullAccessValue | ForEach-Object {
                                        Set-AccessPermission -ShareName $Name -AccessPermission "Full" -Username $_
                                     }
            }

            $smbShareAccessValues = Get-SmbShareAccess -Name $Name

            # Remove explicit deny
            $smbShareAccessValues | Where-Object {$_.AccessControlType  -eq 'Deny'} `
                                    | ForEach-Object {
                                        Remove-AccessPermission -ShareName $Name -UserName $_.AccountName -AccessPermission No
                                        }


            if ($NoAccess -ne $null)
            {
                # Add explicit deny
                $noAccessValue | ForEach-Object {
                                      Set-AccessPermission -ShareName $Name -AccessPermission "No" -Username $_
                                   }
            }
        }
    }
    else
    {
        Write-Verbose "Removing share $Name to ensure it is Absent"
        Remove-SmbShare -name $Name -Force
    }
}

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [parameter(Mandatory = $true)]
        [System.String]
        $Path,

        [System.String]
        $Description,

        [System.String[]]
        $ChangeAccess,

        [System.UInt32]
        $ConcurrentUserLimit,

        [System.Boolean]
        $EncryptData,

        [ValidateSet("AccessBased","Unrestricted")]
        [System.String]
        $FolderEnumerationMode,

        [ValidateSet("None","Manual","Programs","Documents","BranchCache")]
        [System.String]
        $CachingMode,

        [System.String[]]
        $FullAccess,

        [System.String[]]
        $NoAccess,

        [System.String[]]
        $ReadAccess,

        [ValidateSet("Present","Absent")]
        [System.String]
        $Ensure = 'Present'
    )

    # Alter the bound parameters, removing anything that is null or emtpy
    $alteredBoundParameters = Set-BoundParameters -boundparameters $PSBoundParameters

    $testResult = $false;
    $share = Get-TargetResource -Name $Name -Path $Path -ErrorAction SilentlyContinue -ErrorVariable ev
    $differences = @()
    if ($Ensure -ne "Absent")
    {
        if ($share.Ensure -eq "Absent")
        {
            $testResult = $false
        }
        elseif ($share.Ensure -eq "Present")
        {
            $Params = 'Name', 'Path', 'Description', 'ChangeAccess', 'ConcurrentUserLimit', 'EncryptData', 'FolderEnumerationMode', 'CachingMode', 'FullAccess', 'NoAccess', 'ReadAccess', 'Ensure'

            # Get all matching parameters from alteredBoundParameters that are in Params
            $matchingParameters = $alteredBoundParameters.Keys.Where({($_ -in $Params)})

            if ($null -ne $matchingParameters)
            {
                foreach ($matchingParameter in $matchingParameters)
                {
                    $differences += Compare-Object -ReferenceObject $alteredBoundParameters[$matchingParameter] -DifferenceObject $share.$matchingParameter #; $differences
                }

                # Check to see if there is anything in $differences
                if (($null -ne $differences) -and ($differences.Length -gt 0))
                {
                    $differences | ForEach-Object {Write-Verbose -Message "$_"}
                    $testResult = $false
                }
                else
                {
                    $testResult = $true
                }
            }
            else
            {
                $testResult = $true
            }
        }
    }
    else
    {
        if ($share.Ensure -eq "Absent")
        {
            $testResult = $true
        }
        else
        {
            $testResult = $false
        }
    }

    $testResult
}

Export-ModuleMember -Function *-TargetResource