QumuloSMB.ps1

<#
    ===========================================================================
    Created by: berat.ulualan@qumulo.com
    Organization: Qumulo, Inc.
    Filename: QumuloSMB.ps1
    Module Name: Qumulo
    Description: PowerShell Script (.ps1) for Qumulo SMB configurations and operations
    -------------------------------------------------------------------------
    MIT License

    Copyright (c) 2022 Qumulo, Inc.

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
    ===========================================================================
#>

function List-QQSMBShares {
    <#
        .SYNOPSIS
            List all SMB shares
        .DESCRIPTION
            List all SMB shares.
        .PARAMETER PopulateTrusteeNames
            Populate trustee names in the response.
        .EXAMPLE
            List-QQSMBShares -PopulateTrusteeNames [-Json]
        .LINK
            https://care.qumulo.com/hc/en-us/articles/360000722428-Create-an-SMB-Share
            https://care.qumulo.com/hc/en-us/articles/115013237727-QQ-CLI-SMB-Shares
            https://care.qumulo.com/hc/en-us/articles/360011328533-SMB-Share-Permissions
            https://care.qumulo.com/hc/en-us/articles/360005375333-Hide-an-SMB-Share
            https://care.qumulo.com/hc/en-us/articles/360041155254-SMB-Host-Restrictions
        #>

    
        # CmdletBinding parameters.
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $False)] [switch]$PopulateTrusteeNames,
            [Parameter(Mandatory = $False)] [switch]$Json
        )
        if ($SkipCertificateCheck -eq 'true') {
            $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
        }
    
        try {
            # Existing BearerToken check
            if (!$global:Credentials) {
                Login-QQCluster
            }
            else {
                if (!($global:Credentials.BearerToken -match "^(session-v1|access-v1)")) {
                    Login-QQCluster
                }
            }
    
            $bearerToken = $global:Credentials.BearerToken
            $clusterName = $global:Credentials.ClusterName
            $portNumber = $global:Credentials.PortNumber
    
            Write-Debug ($global:Credentials | ConvertTo-Json -Depth 10)
    
            $TokenHeader = @{
                Authorization = "Bearer $bearerToken"
            }
    
            # API url definition
            $url = "/v3/smb/shares/"
    
            if ($PopulateTrusteeNames) {
                $url += "?populate-trustee-names=true"
            }
            else {
                $url += "?populate-trustee-names=false"
            }
    
            # API call run
            try {
                $response = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60 -ErrorAction:Stop
    
                # Response
                if ($Json) {
                    return @($response.entries) | ConvertTo-Json -Depth 10
                }
                else {
                    return $response.entries
                }
            }
            catch {
                $_.Exception.Response
            }
        }
        catch {
            $_.Exception.Response
        }
    }
    
    function List-QQSMBShare {
    <#
        .SYNOPSIS
            List a SMB share
        .DESCRIPTION
            Retrieve the specified SMB share.
        .PARAMETER ShareId [ID]
            A unique identifier of the SMB share (share ID)
        .PARAMETER ShareName [SHARE_NAME]
            A unique identifier of the SMB share name
        .PARAMETER TenantId [TENANT_ID]
            ID of the tenant to get the share from. Only used if using the -ShareName argument.
        .EXAMPLE
            List-QQSMBShares -Id [ID] | -ShareName [NAME] -TenantId [TENANT_ID] [-Json]
        .LINK
            https://care.qumulo.com/hc/en-us/articles/360000722428-Create-an-SMB-Share
            https://care.qumulo.com/hc/en-us/articles/115013237727-QQ-CLI-SMB-Shares
            https://care.qumulo.com/hc/en-us/articles/360011328533-SMB-Share-Permissions
            https://care.qumulo.com/hc/en-us/articles/360005375333-Hide-an-SMB-Share
            https://care.qumulo.com/hc/en-us/articles/360041155254-SMB-Host-Restrictions
        #>

        # CmdletBinding parameters.
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $True,ParameterSetName = "Id")] [string]$ShareId,
            [Parameter(Mandatory = $True,ParameterSetName = "Name")] [string]$ShareName,
            [Parameter(Mandatory = $True,ParameterSetName = "Name")] [int16]$TenantID,
            [Parameter(Mandatory = $False)] [switch]$Json
        )
        if ($SkipCertificateCheck -eq 'true') {
            $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
        }
    
    
        try {
            $foundExport = 0
            # Existing BearerToken check
            if (!$global:Credentials) {
                Login-QQCluster
            }
            else {
                if (!($global:Credentials.BearerToken -match "^(session-v1|access-v1)")) {
                    Login-QQCluster
                }
            }
    
            $bearerToken = $global:Credentials.BearerToken
            $clusterName = $global:Credentials.ClusterName
            $portNumber = $global:Credentials.PortNumber
    
            Write-Debug ($global:Credentials | ConvertTo-Json -Depth 10)
    
            $TokenHeader = @{
                Authorization = "Bearer $bearerToken"
            }
    
    
            $url = "/v3/smb/shares/"
    
            # API url definition
            if ($ShareId) {
                $url += "$ShareId"
            }
            elseif ($ShareName) {
                # API call run
                try {
                    $response = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60 -ErrorAction:Stop
    
                    # Response
                    $smbShares = $response.entries
    
                    foreach ($share in $smbShares) {
                        if (($ShareName -eq $share.share_name) -and ($TenantID -eq $share.tenant_id)) {
                            $ShareId = $share.id
                            $url += $ShareId
                            $foundExport = 1
                        }
                    }
    
                    if ($foundExport -eq 0) {
                        Write-Error "No matching share found. Check the share name and tenant id."
                        return
                    }
                }
                catch {
                    $_.Exception.Response
                }
            }
    
            # API call run
            try {
                $response = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60 -ErrorAction:Stop
    
                if ($Json) {
                    return @($response) | ConvertTo-Json -Depth 10
                }
                else {
                    return $response
                }
            }
            catch {
                $_.Exception.Response
            }
        }
        catch {
            $_.Exception.Response
        }
    }
    
    function Add-QQSMBShare {
    <#
        .SYNOPSIS
            Add a new SMB share
        .DESCRIPTION
            Add an SMB share with given options.
        .PARAMETER ShareName [SHARE_NAME]
            The SMB share name
        .PARAMETER FsPath [FS_PATH]
            The filesystem path to SMB share
        .PARAMETER TenantId [TENANT_ID]
            ID of the tenant to get the share from. Only used if using the -ShareName argument.
        .PARAMETER Description [DESCRIPTION]
            Description of this SMB share
        .PARAMETER AccessBasedEnumerationEnabled [$true|$false]
            Enable Access-based Enumeration on this SMB share
        .PARAMETER CreateFSPath [$true|$false]
            Specifies whether the file system path can be created if it does not already exist.
        .PARAMETER DefaultFileCreateMode DEFAULT_FILE_CREATE_MODE]
            Default POSIX file create mode bits on this SMB share (octal, default 0644 if this field is empty)
        .PARAMETER DefaultDirectoryCreateMode DEFAULT_DIRECTORY_CREATE_MODE]
            Default POSIX directory create mode bits on this SMB share (octal, default 0755 if this field is empty)
        .PARAMETER RequireEncryption {true,false}]
            Require all traffic to this share to be encrypted. Clients without encryption capabilities will not be able to connect. Default is false if this field is empty.
        .PARAMETER NoAccess
            Grant no access.
        .PARAMETER ReadOnly
            Grant everyone except guest read-only access.
        .PARAMETER AllAccess
            Grant everyone except guest full access.
        .PARAMETER GrantReadAccess [TRUSTEE]
            Grant read access to these trustees. e.g. Everyone, uid:1000, gid:1001, sid:S-1-5-2-3-4, or auth_id:500
        .PARAMETER GrantReadWriteAccess [TRUSTEE ...]
            Grant read-write access to these trustees.
        .PARAMETER GrantAllAccess [TRUSTEE ...]
            Grant all access to these trustees.
        .PARAMETER DenyAccess [TRUSTEE ...]
            Deny all access to these trustees.
        .PARAMETER FullControlHosts [RANGE ...]
            Address ranges which should be permitted all access that is also granted by share permissions and file permissions. May be individual IP addresses, CIDR masks (e.g. 10.1.2.0/24), or ranges (e.g. 10.2.3.23-47, fd00::42:1fff-c000).
        .PARAMETER ReadOnlyHosts [RANGE ...]
            Address ranges which should be permitted read-only access at most.
        .PARAMETER DenyHosts [RANGE ...]
            Address ranges which should be denied access to this share, regardless of other permissions.
        .PARAMETER DenyAllHosts
            Deny all access to this share.
        .EXAMPLE
            Add-QQSMBShares -ShareName [NAME] -TenantId [TENANT_ID]-FsPath [FS_PATH]
                [-Description DESCRIPTION]
                [-AccessBasedEnumerationEnabled {true,false}]
                [-CreateFSPath {true,false}]
                [-DefaultFileCreateMode DEFAULT_FILE_CREATE_MODE]
                [-DefaultDirectoryCreateMode DEFAULT_DIRECTORY_CREATE_MODE]
                [-RequireEncryption {true,false}]
                [-NoAccess | ReadOnly | AllAccess]
                [-GrantReadAccess TRUSTEE [TRUSTEE ...]
                [-GrantReadWriteAccess TRUSTEE [TRUSTEE ...]
                [-GrantAllAccess TRUSTEE [TRUSTEE ...]
                [-DenyAccess TRUSTEE [TRUSTEE ...]
                [-FullControlHosts RANGE [RANGE ...]
                [-ReadOnlyHosts RANGE [RANGE ...]
                [-DenyHosts RANGE [RANGE ...]
                [-DenyAllHosts]
                [-Json]
            .LINK
                https://care.qumulo.com/hc/en-us/articles/360000722428-Create-an-SMB-Share
                https://care.qumulo.com/hc/en-us/articles/115013237727-QQ-CLI-SMB-Shares
                https://care.qumulo.com/hc/en-us/articles/360011328533-SMB-Share-Permissions
                https://care.qumulo.com/hc/en-us/articles/360005375333-Hide-an-SMB-Share
                https://care.qumulo.com/hc/en-us/articles/360041155254-SMB-Host-Restrictions
        #>

        # CmdletBinding parameters.
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $True,ParameterSetName = "Name")] [string]$ShareName,
            [Parameter(Mandatory = $True,ParameterSetName = "Name")] [int]$TenantID = 1,
            [Parameter(Mandatory = $True)] [string]$fsPath,
            [Parameter(Mandatory = $False)] [bool]$CreateFSPath = $False,
            [Parameter(Mandatory = $False)] [string]$Description,
            [Parameter(Mandatory = $False)] [string]$DefaultFileCreateMode = "0644",
            [Parameter(Mandatory = $False)] [string]$DefaultDirCreateMode = "0755",
            [Parameter(Mandatory = $False)] [bool]$AccessBasedEnumaration = $False,
            [Parameter(Mandatory = $False)] [bool]$RequireEncryption = $False,
            [Parameter(Mandatory = $False)] [switch]$NoAccess,
            [Parameter(Mandatory = $False)] [switch]$DenyAllHosts,
            [Parameter(Mandatory = $False)] [switch]$ReadOnly,
            [Parameter(Mandatory = $False)] [switch]$AllAccess,
            [Parameter(Mandatory = $False)] [array]$GrantReadAccess,
            [Parameter(Mandatory = $False)] [array]$GrantReadWriteAccess,
            [Parameter(Mandatory = $False)] [array]$GrantAllAccess,
            [Parameter(Mandatory = $False)] [array]$DenyAccess,
            [Parameter(Mandatory = $False)] [array]$ReadOnlyHosts,
            [Parameter(Mandatory = $False)] [array]$DenyHosts,
            [Parameter(Mandatory = $False)] [array]$FullControlHosts,
            [Parameter(Mandatory = $False)] [switch]$Json
        )
        if ($SkipCertificateCheck -eq 'true') {
            $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
        }
    
        try {
            # Existing BearerToken check
            if (!$global:Credentials) {
                Login-QQCluster
            }
            else {
                if (!($global:Credentials.BearerToken -match "^(session-v1|access-v1)")) {
                    Login-QQCluster
                }
            }
    
            $bearerToken = $global:Credentials.BearerToken
            $clusterName = $global:Credentials.ClusterName
            $portNumber = $global:Credentials.PortNumber
    
            Write-Debug ($global:Credentials | ConvertTo-Json -Depth 10)
    
            $TokenHeader = @{
                Authorization = "Bearer $bearerToken"
            }
    
            # API url definition
            if ($CreateFSPath) {
                $url = "/v3/smb/shares/?allow-fs-path-create=true"
            }
            else {
                $url = "/v3/smb/shares/?allow-fs-path-create=false"
            }
    
            # Trustee (User & Group) Share Permissions
            if ($noAccess) {
                $permissions = @()
            }
            else {
                $permissions = @()
                if ($readOnly) {
                    $trusteeHash = @{ name = "Everyone" }
                    $permissions += (
                        @{
                            type = "ALLOWED"
                            trustee = $trusteeHash
                            rights = @(
                                "READ"
                            )
                        }
                    )
                }
    
                if ($allAccess) {
                    $trusteeHash = @{ name = "Everyone" }
                    $permissions += (
                        @{
                            type = "ALLOWED"
                            trustee = $trusteeHash
                            rights = @(
                                "ALL"
                            )
                        }
                    )
                }
                if ($GrantReadAccess) {
                    foreach ($trustee in $GrantReadAccess) {
                        if ($trustee.Contains(':'))
                        {
                            $trusteeArray = $trustee.Split(":")
                            $trusteeHash = @{ $trusteeArray[0] = $trusteeArray[1] }
                        }
                        else {
                            $trusteeHash = @{ name = $trustee }
                        }
                        $permissions += (
                            @{
                                type = "ALLOWED"
                                trustee = $trusteeHash
                                rights = @(
                                    "READ"
                                )
                            }
                        )
                    }
                }
                if ($GrantReadWriteAccess) {
                    foreach ($trustee in $GrantReadWriteAccess) {
                        if ($trustee.Contains(':'))
                        {
                            $trusteeArray = $trustee.Split(":")
                            $trusteeHash = @{ $trusteeArray[0] = $trusteeArray[1] }
                        }
                        else {
                            $trusteeHash = @{ name = $trustee }
                        }
                        $permissions += (
                            @{
                                type = "ALLOWED"
                                trustee = $trusteeHash
                                rights = @(
                                    "READ",
                                    "WRITE"
                                )
                            }
                        )
                    }
                }
                if ($grantAllAccess) {
                    foreach ($trustee in $grantAllAccess) {
                        if ($trustee.Contains(':'))
                        {
                            $trusteeArray = $trustee.Split(":")
                            $trusteeHash = @{ $trusteeArray[0] = $trusteeArray[1] }
                        }
                        else {
                            $trusteeHash = @{ name = $trustee }
                        }
                        $permissions += (
                            @{
                                type = "ALLOWED"
                                trustee = $trusteeHash
                                rights = @(
                                    "ALL"
                                )
                            }
                        )
                    }
                }
                if ($DenyAccess) {
                    foreach ($trustee in $DenyAccess) {
                        if ($trustee.Contains(':'))
                        {
                            $trusteeArray = $trustee.Split(":")
                            $trusteeHash = @{ $trusteeArray[0] = $trusteeArray[1] }
                        }
                        else {
                            $trusteeHash = @{ name = $trustee }
                        }
                        $permissions += (
                            @{
                                type = "DENIED"
                                trustee = $trusteeHash
                                rights = @(
                                    "ALL"
                                )
                            }
                        )
                    }
                }
            }
    
            # Host Restriction Permissions
            if (!$ReadOnlyHosts -or !$DenyHosts -or !$FullControlHosts -or !$DenyAllHosts)
            {
                $networkPermissions = @(
                    @{
                        type = "ALLOWED"
                        address_ranges = @()
                        rights = @(
                            "READ",
                            "WRITE",
                            "CHANGE_PERMISSIONS"
                        )
                    }
                )
            }
            else {
                $networkPermissions = @()
                if ($ReadOnlyHosts) {
                    $networkPermissions += (
                        @{
                            type = "DENIED"
                            address_ranges = $ReadOnlyHosts
                            rights = @(
                                "WRITE",
                                "CHANGE_PERMISSIONS"
                            )
                        },
                        @{
                            type = "ALLOWED"
                            address_ranges = $ReadOnlyHosts
                            rights = @(
                                "READ"
                            )
                        }
                    )
                }
                if ($DenyHosts) {
                    $networkPermissions += (
                        @{
                            type = "DENIED"
                            address_ranges = $DenyHosts
                            rights = @(
                                "ALL"
                            )
                        }
                    )
                }
                if ($FullControlHosts) {
                    $networkPermissions += (
                        @{
                            type = "ALLOWED"
                            address_ranges = $FullControlHosts
                            rights = @(
                                "ALL"
                            )
                        }
                    )
                }
                if ($DenyAllHosts) {
                    $networkPermissions += (
                        @{
                            type = "DENIED"
                            address_ranges = $DenyAllHosts
                            rights = @(
                                "ALL"
                            )
                        }
                    )
                }
    
            }
    
            # API Request body
            $body = @{
                "share_name" = $ShareName
                "tenant_id" = $TenantID
                "fs_path" = $FsPath
                "description" = $Description
                "permissions" = $Permissions
                "network_permissions" = $networkPermissions
                "access_based_enumeration_enabled" = $AccessBasedEnumaration
                "default_file_create_mode" = $DefaultFileCreateMode
                "default_directory_create_mode" = $DefaultDirCreateMode
                "require_encryption" = $RequireEncryption
            }
    
            Write-Debug ($body | ConvertTo-Json -Depth 10)
    
            # API call run
            try {
                $response = Invoke-RestMethod -SkipCertificateCheck -Method 'POST' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -Body ($body | ConvertTo-Json -Depth 10) -TimeoutSec 60 -ErrorAction:Stop
                # Response
                if ($Json) {
                    return @($response) | ConvertTo-Json -Depth 10
                }
                else {
                    return $response
                }
            }
            catch {
                $_.Exception.Response
            }
        }
        catch {
            $_.Exception.Response
        }
    }
    
    function Delete-QQSMBShare {
    <#
        .SYNOPSIS
            Delete a SMB share
        .DESCRIPTION
            Delete an SMB share. Not undoable.
        .PARAMETER ShareId [ID]
            A unique identifier of the SMB share ID
        .PARAMETER ShareName [SHARE_NAME]
            A unique identifier of the SMB share name
        .PARAMETER TenantId [TENANT_ID]
            ID of the tenant to get the share from. Only used if using the -ShareName argument.
        .EXAMPLE
            Delete-QQSMBShares -Id [ID] | -ShareName [NAME] -TenantId [TENANT_ID]
        .LINK
            https://care.qumulo.com/hc/en-us/articles/360000722428-Create-an-SMB-Share
            https://care.qumulo.com/hc/en-us/articles/115013237727-QQ-CLI-SMB-Shares
            https://care.qumulo.com/hc/en-us/articles/360011328533-SMB-Share-Permissions
            https://care.qumulo.com/hc/en-us/articles/360005375333-Hide-an-SMB-Share
            https://care.qumulo.com/hc/en-us/articles/360041155254-SMB-Host-Restrictions
        #>

        # CmdletBinding parameters.
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $True,ParameterSetName = "Id")] [string]$ShareId,
            [Parameter(Mandatory = $True,ParameterSetName = "Name")] [string]$ShareName,
            [Parameter(Mandatory = $True,ParameterSetName = "Name")] [int16]$TenantID
        )
        if ($SkipCertificateCheck -eq 'true') {
            $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
        }
    
        try {
            $foundExport = 0
    
            # Existing BearerToken check
            if (!$global:Credentials) {
                Login-QQCluster
            }
            else {
                if (!($global:Credentials.BearerToken -match "^(session-v1|access-v1)")) {
                    Login-QQCluster
                }
            }
    
            $bearerToken = $global:Credentials.BearerToken
            $clusterName = $global:Credentials.ClusterName
            $portNumber = $global:Credentials.PortNumber
    
            Write-Debug ($global:Credentials | ConvertTo-Json -Depth 10)
    
            $TokenHeader = @{
                Authorization = "Bearer $bearerToken"
            }
    
            # API url definition
            $url = "/v3/smb/shares/"
    
    
            if ($ShareId) {
                $url += $ShareId
            }
            elseif ($ShareName) {
                # API call run
                try {
                    $response = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60 -ErrorAction:Stop
    
                    # Response
                    $smbShares = $response.entries
    
                    foreach ($share in $smbShares) {
                        if (($ShareName -eq $share.share_name) -and ($TenantID -eq $share.tenant_id)) {
                            $ShareId = $share.id
                            $url += $ShareId
                            $foundExport = 1
                        }
                    }
    
                    if ($foundExport -eq 0) {
                        Write-Error "No matching share found. Check the share name and tenant id."
                        return
                    }
                }
                catch {
                    $_.Exception.Response
                }
            }
    
            #API call run
            try {
                $response = Invoke-RestMethod -SkipCertificateCheck -Method 'DELETE' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60 -ErrorAction:Stop
    
                # Response
                return ("SMB share ($ShareId) was deleted successfully.")
            }
            catch {
                $_.Exception.Response
            }
        }
        catch {
            $_.Exception.Response
        }
    
    
    }
    
    function Add-QQSMBSharePermission {
    <#
        .SYNOPSIS
            Add new SMB share permissions
        .DESCRIPTION
            Add new SMB share permission
        .PARAMETER ShareId [ID]
            The SMB share ID
        .PARAMETER ShareName [SHARE_NAME]
            The SMB share name
        .PARAMETER TenantId [TENANT_ID]
            ID of the tenant to get the share from. Only used if using the -ShareName argument.
        .PARAMETER Type [Allowed|Denied]
            SMB Share permission type
        .PARAMETER Rights [None|Read,Write,Change_permissions|All]
            SMB Share permission rights.
        .PARAMETER Trustee [TRUSTEE]
            Trustees. e.g. Everyone, uid:1000, gid:1001, sid:S-1-5-2-3-4, or auth_id:500
        .EXAMPLE
            Add-QQSMBSharePermissions -ShareName [NAME] -TenantId [TENANT_ID] | -Id [ID]
                -Type [Allowed|Denied]
                -Rights [None|Read,Write,Change_permissions|All]
                -Trustee [TRUSTEE]
                [-Json]
            #>

        # CmdletBinding parameters.
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $True,ParameterSetName = "Id")] [string]$ShareId,
            [Parameter(Mandatory = $True,ParameterSetName = "Name")] [string]$ShareName,
            [Parameter(Mandatory = $True,ParameterSetName = "Name")] [int16]$TenantID,
            [Parameter(Mandatory = $True)] [string]$Trustee,
            [Parameter(Mandatory = $True)][ValidateSet("Allowed","Denied")] [string]$Type,
            [Parameter(Mandatory = $True)][ValidateSet("None","Read","Write","Change_permissions","All")] [array]$Rights,
            [Parameter(Mandatory = $False)] [switch]$Json
        )
        if ($SkipCertificateCheck -eq 'true') {
            $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
        }
    
        try {
            # Existing BearerToken check
            if (!$global:Credentials) {
                Login-QQCluster
            }
            else {
                if (!($global:Credentials.BearerToken -match "^(session-v1|access-v1)")) {
                    Login-QQCluster
                }
            }
    
            $bearerToken = $global:Credentials.BearerToken
            $clusterName = $global:Credentials.ClusterName
            $portNumber = $global:Credentials.PortNumber
    
            Write-Debug ($global:Credentials | ConvertTo-Json -Depth 10)
    
            $TokenHeader = @{
                Authorization = "Bearer $bearerToken"
            }
    
            # Share Name -> ID conversion
            $url = "/v3/smb/shares/"
            if (!$ShareId) {
                try {
                    $response = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60 -ErrorAction:Stop
    
                    # Response
                    $smbShares = $response.entries
    
                    foreach ($share in $smbShares) {
                        if (($ShareName -eq $share.share_name) -and ($TenantID -eq $share.tenant_id)) {
                            $ShareId = $share.id
                            $url += $ShareId
                            $foundExport = 1
                        }
                    }
    
                    if ($foundExport -eq 0) {
                        Write-Error "No matching share found. Check the share name and tenant id."
                        return
                    }
                }
                catch {
                    $_.Exception.Response
                }
            }

            # Local function to match permission
            function Test-PermissionMatch {
                param($Permission, $Trustee, $Type, $Rights)

                # Normalize "All" rights to explicit set
                if ($Rights.Count -eq 1 -and $Rights[0] -eq "All") {
                    $Rights = @("Write", "Read", "Change_permissions")
                }

                $trusteeMatch = $false
                if ($Trustee.Contains(':')) {
                    $key, $value = $Trustee.Split(":", 2)
                    $trusteeMatch = $Permission.trustee.$key -eq $value
                } else {
                    $trusteeMatch = $Permission.trustee.name -ieq $Trustee
                }

                $typeMatch = $Permission.type -ieq $Type
                
                $permRights = $Permission.rights | ForEach-Object { $_.ToUpper() } | Sort-Object
                $inputRights = $Rights | ForEach-Object { $_.ToUpper() } | Sort-Object
                $rightsMatch = ($permRights -join ',') -eq ($inputRights -join ',')

                return ($trusteeMatch -and $typeMatch -and $rightsMatch)
            }

    
    
            try {
                # API url definition
                $response = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60 -ErrorAction:Stop
    

                # API Request body
                $permissions = $response.permissions

    
                # Trustee identification
                if ($trustee.Contains(':'))
                {
                    $trusteeArray = $trustee.Split(":")
                    $trusteeHash = @{ $trusteeArray[0] = $trusteeArray[1] }
                }
                else {
                    $trusteeHash = @{ name = $trustee }
                }
    
                $newRights = @()
                foreach ($right in $Rights) {
                    $newRights += $right.ToUpper()
                }

                # Avoid adding duplicates
                $duplicate = $false
                foreach ($perm in $permissions) {
                    if (Test-PermissionMatch -Permission $perm -Trustee $Trustee -Type $Type -Rights $Rights) {
                        $duplicate = $true
                        break
                    }
                }

                if ($duplicate) {
                    Write-Warning "The specified permission already exists and will not be added again."
                    return
                }
                $permissions += @(
                    @{
                        type = $Type.ToUpper()
                        trustee = $trusteeHash
                        rights = $newRights
                    }
                )
    
                $body = @{
                    "permissions" = $permissions
                }
    
                Write-Debug ($body | ConvertTo-Json -Depth 10)
    
                # API call run
                try {
                    $response = Invoke-RestMethod -SkipCertificateCheck -Method 'PATCH' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -Body ($body | ConvertTo-Json -Depth 10) -TimeoutSec 60 -ErrorAction:Stop
    
                    # Response
                    if ($Json) {
                        return @($response) | ConvertTo-Json -Depth 10
                    }
                    else {
                        #return $response
                        return
                    }
                }
                catch {
                    $_.Exception.Response
                }
            }
            catch {
                $_.Exception.Response
            }
        }
        catch {
            $_.Exception.Response
        }
    }
    
    function Remove-QQSMBSharePermission {
        <#
        .SYNOPSIS
            Remove matched SMB share permissions with precise matching
        #>

        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $True, ParameterSetName = "Id")] [string]$ShareId,
            [Parameter(Mandatory = $True, ParameterSetName = "Name")] [string]$ShareName,
            [Parameter(Mandatory = $True, ParameterSetName = "Name")] [int16]$TenantID,
            [Parameter(Mandatory = $True)] [string]$Trustee,
            [Parameter(Mandatory = $True)][ValidateSet("Allowed","Denied")] [string]$Type,
            [Parameter(Mandatory = $True)][ValidateSet("None","Read","Write","Change_permissions","All")] [array]$Rights,
            [Parameter(Mandatory = $False)] [switch]$Json
        )
    
        try {
            # Authenticate to the cluster
            if (!$global:Credentials) {
                Login-QQCluster
            }
            else {
                if (!($global:Credentials.BearerToken -match "^(session-v1|access-v1)")) {
                    Login-QQCluster
                }
            }
    
            $bearerToken = $global:Credentials.BearerToken
            $clusterName = $global:Credentials.ClusterName
            $portNumber = $global:Credentials.PortNumber
    
            $TokenHeader = @{
                Authorization = "Bearer $bearerToken"
            }
    
            $url = "/v3/smb/shares/"
            
            # Share Name -> ID conversion if needed
            if (!$ShareId) {
                $response = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60
    
                $smbShares = $response.entries
                $matchingShare = $smbShares | Where-Object { $_.share_name -eq $ShareName -and $_.tenant_id -eq $TenantID }
    
                if (!$matchingShare) {
                    Write-Error "No matching share found. Check the share name and tenant id."
                    return
                }
    
                $ShareId = $matchingShare.id
                $url += $ShareId
            }
            else {
                $url += $ShareId
            }
    
            $url += "?allow-fs-path-create=false"
    
            # Get current permissions
            $getResponse = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60
            $permissions = $getResponse.permissions
    
            # Enhanced debugging function
            function Test-PermissionMatch {
                param($Permission, $MatchTrustee, $MatchType, $MatchRights, [switch]$Verbose)
    
                # Trustee matching with detailed diagnostics
                $trusteeMatch = $false
                $trusteeInfo = ""
                if ($MatchTrustee.Contains(':')) {
                    $key, $value = $MatchTrustee.Split(":", 2)
                    $trusteeMatch = $Permission.trustee.$key -eq $value
                    $trusteeInfo = "Matching $key with value $value. Current trustee: $($Permission.trustee.$key)"
                } else {
                    $trusteeMatch = $Permission.trustee.Name -ieq $MatchTrustee
                    $trusteeInfo = "Matching name '$MatchTrustee'. Current trustee name: $($Permission.trustee.Name)"
                }
    
                # Type matching with diagnostics
                $typeMatch = $Permission.type -ieq $MatchType
                $typeInfo = "Matching type '$MatchType'. Current type: $($Permission.type)"
    
                # Rights matching with precise comparison
                $permRights = $Permission.rights | ForEach-Object { $_.ToUpper() }
                $inputRights = $MatchRights | ForEach-Object { $_.ToUpper() }
                
                # Sort the rights for consistent comparison
                $sortedPermRights = $permRights | Sort-Object
                $sortedInputRights = $inputRights | Sort-Object
                
                $rightsMatch = (@($sortedPermRights) -join ',') -eq (@($sortedInputRights) -join ',')
                $rightsInfo = "Matching rights: Expected $($inputRights -join ','), Current $($permRights -join ',')"
    

                    Write-Debug "Trustee Check: $trusteeMatch - $trusteeInfo"
                    Write-Debug "Type Check: $typeMatch - $typeInfo"
                    Write-Debug "Rights Check: $rightsMatch - $rightsInfo"
                    Write-Debug "Sorted Perm Rights: $($sortedPermRights -join ',')"
                    Write-Debug "Sorted Input Rights: $($sortedInputRights -join ',')"

    
                # Return match result
                return ($trusteeMatch -and $typeMatch -and $rightsMatch)
            }
    
            # Prepare to track matching and non-matching permissions
            $matchingPermissions = @()
            $nonMatchingPermissions = @()
    
            # Detailed permission filtering
            foreach ($perm in $permissions) {
                $isMatch = Test-PermissionMatch $perm $Trustee $Type $Rights
                if ($isMatch) {
                    $matchingPermissions += $perm
                } else {
                    $nonMatchingPermissions += $perm
                }
            }
    
            # Debugging output
            Write-Debug "Matching Permissions Count: $($matchingPermissions.Count)"
            Write-Debug "Non-Matching Permissions Count: $($nonMatchingPermissions.Count)"
            
            Write-Debug "`nMatching Permissions Details:"
            $matchingPermissions | ForEach-Object {
                Write-Debug ($_ | ConvertTo-Json -Depth 5)
            }

    
            # If no matching permissions found
            if ($matchingPermissions.Count -eq 0) {
                Write-Error "No matching permission found to remove."
                
                # Detailed permission mismatch explanation
                Write-Debug "`nDetailed Permission Check:"
                foreach ($perm in $permissions) {
                    Test-PermissionMatch $perm $Trustee $Type $Rights -Verbose
                }
                return
            }
    
            # Prepare API request body with non-matching permissions
            $body = @{
                "permissions" = $nonMatchingPermissions
            }
    
            # Update permissions
            $updateResponse = Invoke-RestMethod -SkipCertificateCheck -Method 'PATCH' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -Body ($body | ConvertTo-Json -Depth 10) -TimeoutSec 60
    
            # Return response
            if ($Json) {
                return $updateResponse | ConvertTo-Json -Depth 10
            }
            else {
                return $updateResponse
            }
        }
        catch {
            Write-Error "An error occurred: $_"
            $_.Exception.Response
        }
    }

    function Get-QQSMBSettings {
    <#
        .SYNOPSIS
            Get SMB settings
        .DESCRIPTION
            Get SMB settings
        .EXAMPLE
            Get-QQSMBSettings [-Json]
        .LINK
            https://care.qumulo.com/hc/en-us/articles/360000722428-Create-an-SMB-Share
            https://care.qumulo.com/hc/en-us/articles/115013237727-QQ-CLI-SMB-Shares
            https://care.qumulo.com/hc/en-us/articles/360011328533-SMB-Share-Permissions
            https://care.qumulo.com/hc/en-us/articles/360005375333-Hide-an-SMB-Share
            https://care.qumulo.com/hc/en-us/articles/360041155254-SMB-Host-Restrictions
        #>

        # CmdletBinding parameters
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $False)] [switch]$Json
        )
        if ($SkipCertificateCheck -eq 'true') {
            $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
        }
    
        try {
            # Existing BearerToken check
            if (!$global:Credentials) {
                Login-QQCluster
            }
            else {
                if (!($global:Credentials.BearerToken -match "^(session-v1|access-v1)")) {
                    Login-QQCluster
                }
            }
    
            $bearerToken = $global:Credentials.BearerToken
            $clusterName = $global:Credentials.ClusterName
            $portNumber = $global:Credentials.PortNumber
    
            Write-Debug ($global:Credentials | ConvertTo-Json -Depth 10)
    
            $TokenHeader = @{
                Authorization = "Bearer $bearerToken"
            }
    
            # API url definition
            $url = "/v1/smb/settings"
    
            # API call run
            try {
                $response = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60 -ErrorAction:Stop
    
                # Response
                if ($Json) {
                    return @($response) | ConvertTo-Json -Depth 10
                }
                else {
                    return $response
                }
            }
            catch {
                $_.Exception.Response
            }
        }
        catch {
            $_.Exception.Response
        }
    }
    
    
    function Modify-QQSMBSettings {
    <#
        .SYNOPSIS
            Set SMB server settings
        .DESCRIPTION
            Partially set settings for the SMB Server.
        .PARAMETER EncryptionMode {none,preferred,required}
            Server encryption mode to set
        .PARAMETER SupportedDialects dialect_1 [dialect_2 ...]
            Space separated set of SMB dialects to allow clients to negotiate. Choose from the following:
                smb2_dialect_2_002, smb2_dialect_2_1,
                smb2_dialect_3_0, smb2_dialect_3_11,
                or use -SupportedDialects ALL to allow all supported dialects.
        .PARAMETER HideSharesFromUnauthorizedHosts {true,false}
            Share listing will omit shares that the requesting host is not authorized to connect to.
        .PARAMETER HideSharesFromUnauthorizedUsers {true,false}
            Share listing will omit shares that the requesting user is not authorized to connect to. Caution: clients that are not configured for passwordless authentication typically list shares using guest privileges; this option will typically hide all shares from such clients.
        .PARAMETER SnapshotDirectoryMode {visible,hidden,disabled}
            If "visible", a special .snapshot directory will appear in directory listings at the root of shares, and be accessible by name in any directory.
            If "hidden", the .snapshot directory will not appear in directory listings, but will still be accessible by name.
            If "disabled", .snapshot directories will not be accessible, and snapshots will only be available via e.g. the Restore Previous Versions dialog on Windows.
        .PARAMETER BypassTraverseChecking {$true,$false}
            Bypass traverse checking for all users and all directories. In other words, a user trying to access /foo/bar who has permissions to bar but no permissions to foo can still access bar. Users still need permissions on foo to see the contents of that directory.
        .PARAMETER SigningRequired {true,false}
            Require that messages must be signed if the user is not guest. Applies to all SMB shares.
        .EXAMPLE
            Modify-QQSMBSettings [-Json]
                -EncryptionMode {none,preferred,required}
                        Server encryption mode to set
                -SupportedDialects dialect_1 [dialect_2 ...]
                -HideSharesFromUnauthorizedHosts {true,false}
                -HideSharesFromUnauthorizedUsers {true,false}
                -SnapshotDirectoryMode {visible,hidden,disabled}
                -BypassTraverseChecking {true,false}
                -SigningRequired {true,false}
            .LINK
                https://care.qumulo.com/hc/en-us/articles/360000722428-Create-an-SMB-Share
                https://care.qumulo.com/hc/en-us/articles/115013237727-QQ-CLI-SMB-Shares
                https://care.qumulo.com/hc/en-us/articles/360011328533-SMB-Share-Permissions
                https://care.qumulo.com/hc/en-us/articles/360005375333-Hide-an-SMB-Share
                https://care.qumulo.com/hc/en-us/articles/360041155254-SMB-Host-Restrictions
        #>

        # CmdletBinding parameters.
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $False)] [switch]$Json,
            [Parameter(Mandatory = $False)][ValidateSet("none","preferred","required")] [string]$EncryptionMode,
            [Parameter(Mandatory = $False)] [bool]$HideSharesFromUnauthorizedHosts,
            [Parameter(Mandatory = $False)] [bool]$HideSharesFromUnauthorizedUsers,
            [Parameter(Mandatory = $False)] [bool]$BypassTraverseChecking,
            [Parameter(Mandatory = $False)] [bool]$SigningRequired,
            [Parameter(Mandatory = $False)][ValidateSet("visible","hidden","disabled")] [string]$SnapshotDirectoryMode,
            [Parameter(Mandatory = $False)][ValidateSet("smb2_dialect_2_002","smb2_dialect_2_1","smb2_dialect_3_0","smb2_dialect_3_11","All")] [array]$SupportedDialects
        )
        if ($SkipCertificateCheck -eq 'true') {
            $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
        }
    
        try {
            # Existing BearerToken check
            if (!$global:Credentials) {
                Login-QQCluster
            }
            else {
                if (!($global:Credentials.BearerToken -match "^(session-v1|access-v1)")) {
                    Login-QQCluster
                }
            }
    
            $bearerToken = $global:Credentials.BearerToken
            $clusterName = $global:Credentials.ClusterName
            $portNumber = $global:Credentials.PortNumber
    
            Write-Debug ($global:Credentials | ConvertTo-Json -Depth 10)
    
            $TokenHeader = @{
                Authorization = "Bearer $bearerToken"
            }
    
            # API Request body
            $body = @{}
    
            if ($EncryptionMode) {
                $body += @{
                    "session_encryption" = $EncryptionMode.ToUpper() }
            }
    
            if ($HideSharesFromUnauthorizedHosts) {
                $body += @{
                    "hide_shares_from_unauthorized_hosts" = $HideSharesFromUnauthorizedHosts
                }
    
            }
    
            if ($HideSharesFromUnauthorizedUsers) {
                $body += @{
                    "hide_shares_from_unauthorized_users" = $HideSharesFromUnauthorizedUsers
                }
            }
    
            if ($BypassTraverseChecking) {
                $body += @{
                    bypass_traverse_checking = $BypassTraverseChecking
                }
            }
    
            if ($SigningRequired) {
                $body += @{
                    "signing_required" = $SigningRequired
                }
            }
    
            if ($SnapshotDirectoryMode) {
                $body += @{
                    "snapshot_directory_mode" = $SnapshotDirectoryMode.ToUpper()
                }
            }
    
            if ($SupportedDialects) {
                if ($SupportedDialects -eq "ALL") {
                    $body += @{
                        "supported_dialects" = @(
                            "SMB2_DIALECT_2_002",
                            "SMB2_DIALECT_2_1",
                            "SMB2_DIALECT_3_0",
                            "SMB2_DIALECT_3_11"
                        )
                    }
                }
                else {
                    $body += @{
                        "supported_dialects" = $SupportedDialects
                    }
                }
            }
    
            # API url definition
            $url = "/v1/smb/settings"
    
            # API call run
            try {
                $response = Invoke-RestMethod -SkipCertificateCheck -Method 'PATCH' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -Body ($body | ConvertTo-Json -Depth 10) -TimeoutSec 60 -ErrorAction:Stop
    
                # Response
                if ($Json) {
                    return @($response) | ConvertTo-Json -Depth 10
                }
                else {
                    return $response
                }
            }
            catch {
                $_.Exception.Response
            }
        }
        catch {
            $_.Exception.Response
        }
    }
    function List-QQSMBFileHandles {
    <#
        .SYNOPSIS
            List SMB open file handles.
        .DESCRIPTION
            List SMB open file handles
        .PARAMETER Path [PATH]
            Path to file.
        .PARAMETER PageSize [PAGE_SIZE]
            Max files to return per request
        .PARAMETER ResolvePaths
            Returns the primary path of the opened file
        
        .EXAMPLE
            List-QQSMBFileHandles -Path PATH -PageSize 10 -ResolvePaths [-Json]
        .LINK
            https://care.qumulo.com/hc/en-us/articles/360044728593-Close-an-Open-SMB-File-via-QQ-CLI
        #>

        # CmdletBinding parameters
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $False)] [string]$Path,
            [Parameter(Mandatory = $False)] [string]$FileNumber,
            [Parameter(Mandatory = $False)] [string]$PageSize,
            [Parameter(Mandatory = $False)] [bool]$ResolvePaths = $False,
            [Parameter(Mandatory = $False)] [switch]$Json
        )
        if ($SkipCertificateCheck -eq 'true') {
            $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
        }
    
        try {
            # Existing BearerToken check
            if (!$global:Credentials) {
                Login-QQCluster
            }
            else {
                if (!($global:Credentials.BearerToken -match "^(session-v1|access-v1)")) {
                    Login-QQCluster
                }
            }
    
            $bearerToken = $global:Credentials.BearerToken
            $clusterName = $global:Credentials.ClusterName
            $portNumber = $global:Credentials.PortNumber
    
            $TokenHeader = @{
                Authorization = "Bearer $bearerToken"
            }
    
            if ($path) {
                $htmlPath = ([uri]::EscapeDataString($path))
                # API url definition
                $url = "/v1/files/$htmlPath/info/attributes"
    
                # API call run
                try {
                    $response = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60 -ErrorAction:Stop
                    $id = $($response.id)
                }
                catch {
                    $_.Exception.Response
                }
            }
    
            # API url definition
            if ($Path) {
                $url = "/v1/smb/files/?file_number=$id&"
            }
            else {
                if ($FileNumber) {
                    $url = "/v1/smb/files/?file_number=$FileNumber&"
                }
                else {
                    $url = "/v1/smb/files/?"
                }
            }
            if ($ResolvePaths) {
                $url += "resolve_paths=True"
            }
            else {
                $url += "resolve_path=False"
            }
    
            if ($PageSize) {
                $url += "&limit=$PageSize"
            }
    
            # API call run
            try {
    
                $response = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60 -ErrorAction:Stop
    
                # Response
                if ($Json) {
                    return @($response) | ConvertTo-Json -Depth 10
                }
                else {
                    return $response
                }
            }
            catch {
                $_.Exception.Response
            }
        }
        catch {
            $_.Exception.Response
        }
    }
    
    function Close-QQSMBFileHandles {
    <#
            .SYNOPSIS
                Force close a specified SMB file handle
            .DESCRIPTION
                Force close a specified SMB file handle
    
                NOTE: This will prevent the client from sending any new requests for
                this file handle, releasing all locks and forcing the client to reopen
                the file. The client will not be given the opportunity to flush cached
                writes. Proceed with caution!
            .PARAMETER Location [LOCATION]
                The location of the file handle to close as returned from List-QQSMBFileHandles
            .EXAMPLE
                Close-QQSMBFileHandles -Location [LOCATION]
            .LINK
                https://care.qumulo.com/hc/en-us/articles/360044728593-Close-an-Open-SMB-File-via-QQ-CLI
            #>

    
        # CmdletBinding parameters
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $True)][ValidateNotNullOrEmpty()] [string]$Location,
            [Parameter(Mandatory = $False)] [switch]$Json
        )
        if ($SkipCertificateCheck -eq 'true') {
            $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
        }
    
        # Existing BearerToken check
        try {
            if (!$global:Credentials) {
                Login-QQCluster
            }
            else {
                if (!($global:Credentials.BearerToken -match "^(session-v1|access-v1)")) {
                    Login-QQCluster
                }
            }
    
            $bearerToken = $global:Credentials.BearerToken
            $clusterName = $global:Credentials.ClusterName
            $portNumber = $global:Credentials.PortNumber
    
            $TokenHeader = @{
                Authorization = "Bearer $bearerToken"
            }
    
            # API Request body
            $body = @()
            $body += (
                @{
                    'file_number' = 0
                    'handle_info' = @{
                        'owner' = '0'
                        'access_mask' = @('MS_ACCESS_FILE_READ_ATTRIBUTES')
                        'version' = 0
                        'location' = $Location
                        'num_byte_range_locks' = 0
                    }
                }
            )
    
            $bodyJson = $body | ConvertTo-Json -Depth 10
    
            Write-Debug ($body | ConvertTo-Json -Depth 10)
    
            # API url definition
            $url = "/v1/smb/files/close"
    
            # API call run
            try {
                $response = Invoke-RestMethod -SkipCertificateCheck -Method 'POST' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -Body ("[" + $bodyJson + "]") -TimeoutSec 60 -ErrorAction:Stop
    
                # Response
                if ($Json) {
                    return @($response) | ConvertTo-Json -Depth 10
                }
                else {
                    return $response
                }
            }
            catch {
                $_.Exception.Response
            }
    
            else {
                return ("Missing parameter!")
            }
        }
        catch {
            $_.Exception.Response
        }
    }
    
    function List-QQSMBSessions {
    <#
        .SYNOPSIS
            List SMB open sessions
        .DESCRIPTION
            List SMB open sessions
        .PARAMETER Identity [IDENTITY]
            List only sessions matching this user's identity in the form of: [1] A name or a SID optionally qualified with a domain prefix (e.g "local:name", "S-1-1-0", "name", "world:Everyone",
            "ldap_user:name", or "ad:name"), or [2] An ID type (e.g. "uid:1001", "auth_id:513", "SID:S-1-1-0").
        .PARAMETER PageSize [PAGE_SIZE]
            Max sessions to return per request
        
        .EXAMPLE
            List-QQSMBSessions -Identity ab:qumulouser -PageSize 10 [-Json]
        .LINK
            https://care.qumulo.com/hc/en-us/articles/360046854394-Close-an-Open-SMB-Session
        #>

        # CmdletBinding parameters
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $False)] [string]$Identity,
            [Parameter(Mandatory = $False)] [string]$PageSize,
            [Parameter(Mandatory = $False)] [switch]$Json
        )
        if ($SkipCertificateCheck -eq 'true') {
            $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
        }
    
        try {
            # Existing BearerToken check
            if (!$global:Credentials) {
                Login-QQCluster
            }
            else {
                if (!($global:Credentials.BearerToken -match "^(session-v1|access-v1)")) {
                    Login-QQCluster
                }
            }
    
            $bearerToken = $global:Credentials.BearerToken
            $clusterName = $global:Credentials.ClusterName
            $portNumber = $global:Credentials.PortNumber
    
            $TokenHeader = @{
                Authorization = "Bearer $bearerToken"
            }
    
            # API url definition
            $url = "/v1/smb/sessions/"
    
    
            if (($PageSize) -and ($Identity)) {
                $htmlIdentity = ([uri]::EscapeDataString($Identity))
                $url += "?limit=$PageSize&?identity=$htmlIdentity"
            }
            else {
                if ($PageSize) {
                    $url += "?limit=$PageSize"
                }
    
                if ($Identity) {
                    $htmlIdentity = ([uri]::EscapeDataString($Identity))
                    $url += "?identity=$htmlIdentity"
                }
            }
    
    
            # API call run
            try {
    
                $response = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60 -ErrorAction:Stop
    
                # Response
                if ($Json) {
                    return @($response) | ConvertTo-Json -Depth 10
                }
                else {
                    return $response
                }
            }
            catch {
                $_.Exception.Response
            }
        }
        catch {
            $_.Exception.Response
        }
    }
    
    function Close-QQSMBSessions {
    <#
            .SYNOPSIS
                Force close SMB sessions matching one or more of a set of filters.
    
            .DESCRIPTION
                Force close SMB sessions matching one or more of a set of filters.
    
                NOTE: This will prevent the client from sending any new requests for
                this session, releasing all locks and forcing the client to
                reauthenticate. The client will not be given the opportunity to flush
                cached writes. Proceed with caution!
            .PARAMETER Location [LOCATION]
                The location of the file handle to close as returned from List-QQSMBSessions
            .PARAMETER Identity [IDENTITY]
                Close only sessions matching this user's identity in the form of: [1] A name or a SID optionally qualified with a domain prefix (e.g "local:name", "S-1-1-0", "name",
                "world:Everyone", "ldap_user:name", or "ad:name"), or [2] An ID type (e.g. "uid:1001","auth_id:513", "SID:S-1-1-0").
            .PARAMETER Ip [IP]
                Close only sessions originating from this ip.
            .EXAMPLE
                Close-QQSMBSessions -Location [LOCATION]
            .LINK
                https://care.qumulo.com/hc/en-us/articles/360046854394-Close-an-Open-SMB-Session
            #>

    
        # CmdletBinding parameters
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $False)] [string]$Location,
            [Parameter(Mandatory = $False)] [string]$Identity,
            [Parameter(Mandatory = $False)] [string]$Ip,
            [Parameter(Mandatory = $False)] [switch]$Json
        )
        if ($SkipCertificateCheck -eq 'true') {
            $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
        }
    
        # Existing BearerToken check
        try {
            if (!$global:Credentials) {
                Login-QQCluster
            }
            else {
                if (!($global:Credentials.BearerToken -match "^(session-v1|access-v1)")) {
                    Login-QQCluster
                }
            }
    
            $bearerToken = $global:Credentials.BearerToken
            $clusterName = $global:Credentials.ClusterName
            $portNumber = $global:Credentials.PortNumber
    
            $TokenHeader = @{
                Authorization = "Bearer $bearerToken"
            }
    
    
            $url = "/v1/smb/sessions/"
            if ($Identity) {
                $htmlIdentity = ([uri]::EscapeDataString($Identity))
                $url += "?identity=$htmlIdentity"
            }
            try {
                $response = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60 -ErrorAction:Stop
                $sessions = $response.session_infos
            }
            catch {
                $_.Exception.Response
            }
    
            # API Request body
    
            $matchedSessions1 = @()
            $matchedSessions2 = @()
            $matchedSessions3 = @()
    
            foreach ($session in $sessions) {
                if ($Ip) {
                    if ($session.originator -eq $Ip) {
                        $matchedSessions1 += $session
                    }
                }
                else {
                    $matchedSessions1 += $session
                }
            }
    
            foreach ($matchedSession1 in $matchedSessions1) {
                if ($Location) {
                    if ($matchedSession1.location -eq $Location) {
                        $matchedSessions2 += $matchedSession1
                    }
                }
                else {
                    $matchedSessions2 += $matchedSession1
                }
            }
            # Write-Debug("Matched Sessions2:")
            # Write-Debug($matchedSessions2 | ConvertTo-Json -Depth 10)
    
            foreach ($matchedSession2 in $matchedSessions2) {
                if ($Identity) {
                    $userCheck = $matchedSession2.User
                    if ($userCheck.ContainsValue($Identity)) {
                        $matchedSessions3 += $matchedSession2
                        Write-Debug ($Identity)
                    }
                }
                else {
                    # $userCheck = $matchedSession2.user
                    # Write-Debug($userCheck | ConvertTo-Json -Depth 10)
                    $matchedSessions3 += $matchedSession2
                }
            }
            # Write-Debug("Matched Sessions3:")
            # Write-Debug($matchedSessions3 | ConvertTo-Json -Depth 10)
    
    
            $body = $matchedSessions3
    
            $bodyJson = $body | ConvertTo-Json -Depth 10
            Write-Debug ($body | ConvertTo-Json -Depth 10)
    
            # API url definition
            $url = "/v1/smb/sessions/close"
    
            # API call run
            try {
                $response = Invoke-RestMethod -SkipCertificateCheck -Method 'POST' -Uri "https://${clusterName}:$portNumber$url" -Headers $TokenHeader -ContentType "application/json" -Body ("[" + $bodyJson + "]") -TimeoutSec 60 -ErrorAction:Stop
    
                # Response
                if ($Json) {
                    return @($response) | ConvertTo-Json -Depth 10
                }
                else {
                    return $response
                }
            }
            catch {
                $_.Exception.Response
            }
    
            else {
                return ("Missing parameter!")
            }
        }
        catch {
            $_.Exception.Response
        }
    }