QumuloNFS.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-QQNFSExports {
<#
    .SYNOPSIS
        List all NFS exports
    .DESCRIPTION
        List all NFS exports.
    .EXAMPLE
        List-QQNFSExports [-Json]
    .LINK

    #>


    # 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.StartsWith("session-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/nfs/exports/"
        # 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 Get-QQNFSExport {
<#
    .SYNOPSIS
        List a NFS export
    .DESCRIPTION
        Retrieve the specified NFS export.
    .PARAMETER ExportId [ID]
        A unique identifier of the NFS export (export ID)
    .PARAMETER ExportPath [EXPORT_PATH]
        A unique identifier of the NFS export path
    .EXAMPLE
        Get-QQNFSExport -ExportId [ID] | -ExportPath [ExportPath] [-Json]
    .LINK

    #>

    # CmdletBinding parameters.
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True,ParameterSetName = "ExportId")] [string]$ExportId,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [string]$ExportPath,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [int16]$TenantID,
        [Parameter(Mandatory = $False)] [switch]$Json
    )
    if ($SkipCertificateCheck -eq 'true') {
        $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
    }

    try {
        $foundShare = 0
        # Existing BearerToken check
        if (!$global:Credentials) {
            Login-QQCluster
        }
        else {
            if (!$global:Credentials.BearerToken.StartsWith("session-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/nfs/exports/"

        # API url definition
        if ($ExportId) {
            $url += $ExportId
        }
        elseif ($ExportPath) {

            # 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
                $nfsExports = $response.entries

                foreach ($export in $nfsExports) {
                    if (($ExportPath -eq $export.export_path) -and ($TenantID -eq $export.tenant_id)) {
                        $ExportId = $export.id
                        $url += $ExportId
                        $foundShare = 1
                    }
                }

                if ($foundShare -eq 0) {
                    Write-Error "No matching export found. Check the export path 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-QQNFSExport {
<#
    .SYNOPSIS
        Add a new NFS export
    .DESCRIPTION
        Add an NFS export with given options.
    .PARAMETER ExportPath [EXPORT_PATH]
        The NFS export path
    .PARAMETER FsPath [FS_PATH]
        The filesystem path to NFS export
    .PARAMETER Description [DESCRIPTION]
        Description of this NFS export
    .PARAMETER CreateFSPath [$true|$false]
        Specifies whether the file system path can be created if it does not already exist.
    .PARAMETER ReadOnly [$true|$false]
        Specifies whether the file system path can be created if it does not already exist.
    .PARAMETER TenantID [TENANT_ID]
        ID of the tenant to add the export to

    .EXAMPLE
        Add-QQNFSExport -ExportPath EXPORT_PATH -FsPath FS_PATH
            [-Description DESCRIPTION]
            [-CreateFSPath {true,false}]
            [-Readonly {true,false}]
            [-TEnantID TENANT_ID]
            [-Json]
        .LINK

    #>

    # CmdletBinding parameters.
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True)] [string]$ExportPath,
        [Parameter(Mandatory = $True)] [string]$fsPath,
        [Parameter(Mandatory = $False)] [bool]$CreateFSPath = $False,
        [Parameter(Mandatory = $False)] [string]$Description,
        [Parameter(Mandatory = $False)] [bool]$ReadOnly = $False,
        [Parameter(Mandatory = $True)] [int16]$TenantID,
        [Parameter(Mandatory = $False)] [switch]$Json
    )
    if ($SkipCertificateCheck -eq 'true') {
        $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
    }

    try {
        $foundShare = 1
        # Existing BearerToken check
        if (!$global:Credentials) {
            Login-QQCluster
        }
        else {
            if (!$global:Credentials.BearerToken.StartsWith("session-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/nfs/exports/"

        # API url definition
        if ($CreateFSPath) {
            $url += "?allow-fs-path-create=true"
        }
        else {
            $url += "?allow-fs-path-create=false"
        }




        # API Request body
        $body = @{
            "export_path" = $ExportPath
            "fs_path" = $FsPath
            "description" = $Description
            "tenant_id" = $TenantID
            "restrictions" = @(@{
                    "host_restrictions" = @()
                    "require_privileged_port" = $false
                    "read_only" = $ReadOnly
                    "user_mapping" = "NFS_MAP_NONE"
                    "map_to_user" = @{
                        "id_type" = "LOCAL_USER"
                        "id_value" = "0"
                    }

                })
        }

        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-QQNFSExport {
<#
    .SYNOPSIS
        Delete a NFS export
    .DESCRIPTION
        Delete an NFS export. Not undoable.
    .PARAMETER ExportId [ID]
        A unique identifier of the SMB share ID
    .PARAMETER ExportPath [ExportPath]
        A unique identifier of the NFS export path
    .EXAMPLE
        Delete-QQNFSExport -Id [ID] | -ExportPath [EXPORT_PATH]
    .LINK

    #>

    # CmdletBinding parameters.
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True,ParameterSetName = "ExportId")] [string]$ExportId,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [string]$ExportPath,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [int16]$TenantID
    )
    if ($SkipCertificateCheck -eq 'true') {
        $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
    }

    try {
        $foundShare = 1
        # Existing BearerToken check
        if (!$global:Credentials) {
            Login-QQCluster
        }
        else {
            if (!$global:Credentials.BearerToken.StartsWith("session-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/nfs/exports/"
        # API url definition
        if ($ExportId) {
            $url += $ExportId
        }
        elseif ($ExportPath) {

            # 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
                $nfsExports = $response.entries
                foreach ($export in $nfsExports) {
                    if (($ExportPath -eq $export.export_path) -and ($TenantID -eq $export.tenant_id)) {
                        $ExportId = $export.id
                        $url += $ExportId
                        $foundShare = 1
                    }
                }

                if ($foundShare -eq 0) {
                    Write-Error "No matching export found. Check the export path 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 ("NFS export ($id) was deleted successfully.")
        }
        catch {
            $_.Exception.Response
        }
    }
    catch {
        $_.Exception.Response
    }


}

function Modify-QQNFSExport {
<#
    .SYNOPSIS
        Modify an NFS export
    .DESCRIPTION
        Modify individual attributes of a NFS export.
    .PARAMETER ExportId [EXPORT_ID]
        The NFS export id
    .PARAMETER ExportPath [EXPORT_PATH]
        The NFS export path
    .PARAMETER NewExportPath [NEW_EXPORT_PATH]
        The new NFS export path
    .PARAMETER NewFsPath [NEW_FS_PATH]
        The new filesystem path to NFS export
    .PARAMETER Description [DESCRIPTION]
        Description of this NFS export
    .PARAMETER TenantId [TENANT_ID]
        ID of the tenant the export is in. Only used if using the -ExportPath argument.
    .PARAMETER NewTenantId [TENANT_ID]
        ID of the tenant the export is in. Only used if using the -ExportPath argument.
    .PARAMETER CreateFSPath [$true|$false]
        Specifies whether the file system path can be created if it does not already exist.
    .EXAMPLE
        Modify-QQNFSExport [-Json]
            -ExportPath EXPORT_PATH -TenantID TENANT_ID | -ExportId EXPORT_ID
            [-Description DESCRIPTION]
            [-CreateFSPath {true,false}]
            [-NewFSPath NEW_FS_PATH]
            [-NewExportPath NEW_EXPORT_PATH]
            [-NewTenantID NewTENANT_ID]
            [-Json]
        .LINK

    #>

    # CmdletBinding parameters.
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True,ParameterSetName = "ExportId")] [string]$ExportId,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [string]$ExportPath,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [int16]$TenantID,
        [Parameter(Mandatory = $False)] [string]$NewFsPath,
        [Parameter(Mandatory = $False)] [bool]$CreateFSPath = $False,
        [Parameter(Mandatory = $False)] [string]$Description,
        [Parameter(Mandatory = $False)] [string]$NewExportPath,
        [Parameter(Mandatory = $False)] [string]$NewTenantID
    )
    if ($SkipCertificateCheck -eq 'true') {
        $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
    }

    try {
        $foundShare = 1
        # Existing BearerToken check
        if (!$global:Credentials) {
            Login-QQCluster
        }
        else {
            if (!$global:Credentials.BearerToken.StartsWith("session-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/nfs/exports/"
        if ($ExportId) {
            $url += $ExportId
        }
        elseif ($ExportPath) {

            # 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
                $nfsExports = $response.entries
                foreach ($export in $nfsExports) {
                    if (($ExportPath -eq $export.export_path) -and ($TenantID -eq $export.tenant_id)) {
                        $ExportId = $export.id
                        $url += $ExportId
                        $foundShare = 1
                    }
                }

                if ($foundShare -eq 0) {
                    Write-Error "No matching export found. Check the export path and tenant id."
                    return
                }
            }
            catch {
                $_.Exception.Response
            }
        }

        if ($CreateFSPath) {
            $url += "?allow-fs-path-create=true"
        }
        else {
            $url += "?allow-fs-path-create=false"
        }

        # API Request body
        $body = @{}



        if ($NewfsPath) {
            $body += @{
                "fs_path" = $NewfsPath
            }
        }
        if ($NewExportPath) {
            $body += @{
                "export_path" = $NewExportPath
            }
        }
        if ($NewTenantID) {
            $body += @{
                "tenant_id" = $NewTenantID
            }
        }
        if ($Description) {
            $body += @{
                "description" = $Description
            }
        }

        # 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 Add-QQNFSExportHostAccess {
<#
        .SYNOPSIS
            Add an access hosts are granted to an export
        .DESCRIPTION
            Add the access hosts are granted to an export
        .PARAMETER ExportId [EXPORT_ID]
            The NFS export id
        .PARAMETER ExportPath [EXPORT_PATH]
            The NFS export path
        .PARAMETER TenantId [TENANT_ID]
            ID of the tenant the export is in. Only used if using the -ExportPath argument.
        .PARAMETER DeleteDefaultHostAccess
            Delete the default host access rule.
        .PARAMETER HostRestrictions [HOSTS]
            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). Export will match all by default.
        .PARAMETER Readonly [$true|$false]
            Export is read-only.
        .PARAMETER RootSquash
            Map access by root to the anonymous user.
        .PARAMETER AllSquash
            Map all access to the anonymous user.
        .PARAMETER AnonLocal
            The name of a local user to squash to.
        .EXAMPLE
            Add-QQNFSExportHostAccess
                -ExportPath EXPORT_PATH -TenantID TENANT_ID | -ExportId EXPORT_ID
                -HostRestriction [HOSTS]
                -ReadOnly $true
                -RootSquash
                -AllSquash
                -AnonLocal
        .LINK
    
        #>

    # CmdletBinding parameters.
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True,ParameterSetName = "ExportId")] [string]$ExportId,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [string]$ExportPath,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [int16]$TenantID,
        [Parameter(Mandatory = $False)] [array]$HostRestrictions,
        [Parameter(Mandatory = $False)] [switch]$DeleteDefaultHostAccess,
        [Parameter(Mandatory = $False)] [switch]$ReadOnly,
        [Parameter(Mandatory = $False)] [switch]$RootSquash,
        [Parameter(Mandatory = $False)] [switch]$AllSquash,
        [Parameter(Mandatory = $False)] [string]$AnonLocal

    )
    if ($SkipCertificateCheck -eq 'true') {
        $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
    }

    try {
        $foundShare = 1
        # Existing BearerToken check
        if (!$global:Credentials) {
            Login-QQCluster
        }
        else {
            if (!$global:Credentials.BearerToken.StartsWith("session-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/nfs/exports/"



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

            $nfsExports = $response.entries



            if ($ExportId) {
                foreach ($export in $nfsExports) {
                    if ($ExportId -eq $export.id) {
                        $existingRestrictions = $export.restrictions
                        $url += $ExportId
                        $url += "?allow-fs-path-create=false"
                    }
                }
            }
            elseif ($ExportPath) {
                # Response
                $nfsExports = $response.entries
                foreach ($export in $nfsExports) {
                    if (($ExportPath -eq $export.export_path) -and ($TenantID -eq $export.tenant_id)) {
                        $ExportId = $export.id
                        $existingRestrictions = $export.restrictions
                        $url += $ExportId
                        $url += "?allow-fs-path-create=false"
                        $foundShare = 1
                    }
                }

                if ($foundShare -eq 0) {
                    Write-Error "No matching export found. Check the export path and tenant id."
                    return
                }
            }
        }
        catch {
            $_.Exception.Response
        }

        Write-Debug ($url)
        # API Request body

        if ($DeleteDefaultHostAccess) {
            $tempRestrictions = @()
            foreach ($restriction in $existingRestrictions) {
                Write-Host ($restriction.host_restrictions[0])
                if (($restriction.host_restrictions.Count -eq 0) -and ($restriction.user_mapping -eq "NFS_MAP_NONE") -and ($restriction.read_only -eq $false))
                {
                    Write-Host ("The default host access rule has been removed...")
                }
                else {
                    $tempRestrictions += $restriction
                }
            }
            $existingRestrictions = $tempRestrictions
        }

        $newRestriction = @{}

        if ($HostRestrictions) {
            $newRestriction += @{
                "host_restrictions" = $HostRestrictions
            }
        }
        if ($RootSquash) {
            $newRestriction += @{
                "user_mapping" = "NFS_MAP_ROOT"
                "map_to_user" = @{
                    "id_type" = "LOCAL_USER"
                    "id_value" = $AnonLocal
                }
            }
        }

        if ($AllSquash) {
            $newRestriction += @{
                "user_mapping" = "NFS_MAP_ALL"
                "map_to_user" = @{
                    "id_type" = "LOCAL_USER"
                    "id_value" = $AnonLocal
                }
            }
        }

        if (-not ($AllSquash -or $RootSquash)) {
            $newRestriction += @{
                "user_mapping" = "NFS_MAP_NONE"
            }
        }

        if ($ReadOnly) {
            $newRestriction += @{
                "read_only" = $true
            }
        }
        else {
            $newRestriction += @{
                "read_only" = $false
            }
        }


        $newRestriction += @{
            "require_privileged_port" = $false
        }

        $existingRestrictions += @($newRestriction)

        $body = @{
            "restrictions" = $existingRestrictions
        }

        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
            }
        }
        catch {
            $_.Exception.Response
        }
    }
    catch {
        $_.Exception.Response
    }
}

function List-QQNFSExportHostAccess {
<#
        .SYNOPSIS
            List the host restrictions of an export
        .DESCRIPTION
            List the host restrictions of an export
        .PARAMETER ExportId [EXPORT_ID]
            The NFS export id
        .PARAMETER ExportPath [EXPORT_PATH]
            The NFS export path
        .PARAMETER TenantId [TENANT_ID]
            ID of the tenant the export is in. Only used if using the -ExportPath argument.
        .EXAMPLE
            Add-QQNFSExportHostAccess
                -ExportPath EXPORT_PATH -TenantID TENANT_ID | -ExportId EXPORT_ID
            .LINK
    
        #>

    # CmdletBinding parameters.
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True,ParameterSetName = "ExportId")] [string]$ExportId,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [string]$ExportPath,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [int16]$TenantID

    )
    if ($SkipCertificateCheck -eq 'true') {
        $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
    }

    try {
        $foundShare = 1
        # Existing BearerToken check
        if (!$global:Credentials) {
            Login-QQCluster
        }
        else {
            if (!$global:Credentials.BearerToken.StartsWith("session-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/nfs/exports/"


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

            $nfsExports = $response.entries

            if ($ExportId) {
                foreach ($export in $nfsExports) {
                    if ($ExportId -eq $export.id) {
                        $existingRestrictions = $export.restrictions
                        Write-Host ($existingRestrictions | ConvertTo-Json -Depth 10)
                    }
                }
            }
            elseif ($ExportPath) {
                # Response
                $nfsExports = $response.entries
                foreach ($export in $nfsExports) {
                    if (($ExportPath -eq $export.export_path) -and ($TenantID -eq $export.tenant_id)) {
                        $existingRestrictions = $export.restrictions
                        $i = 1
                        $existingRestrictionsWPosition = @()
                        foreach ($restriction in $existingRestrictions)
                        {

                            $restriction | Add-Member -MemberType NoteProperty -Name "position" -Value $i.ToString()

                            $existingRestrictionsWPosition += $restriction

                            $i = $i + 1
                        }
                        Write-Host ($existingRestrictionsWPosition | ConvertTo-Json -Depth 10)
                        $foundShare = 1
                    }
                }

                if ($foundShare -eq 0) {
                    Write-Error "No matching export found. Check the export path and tenant id."
                    return
                }
            }
        }
        catch {
            $_.Exception.Response
        }
    }
    catch {
        $_.Exception.Response
    }
}


function Modify-QQNFSExportHostAccess {
<#
        .SYNOPSIS
            Modify an access hosts are granted to an export
        .DESCRIPTION
            Modify the access hosts are granted to an export
        .PARAMETER ExportId [EXPORT_ID]
            The NFS export id
        .PARAMETER ExportPath [EXPORT_PATH]
            The NFS export path
        .PARAMETER TenantId [TENANT_ID]
            ID of the tenant the export is in. Only used if using the -ExportPath argument.
        .PARAMETER Position [POSITION]
            The position value of the host restriction that you can get List-QQNFSExportHostAccess
        .PARAMETER HostRestrictions [HOSTS]
            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). Export will match all by default.
        .PARAMETER Readonly [$true|$false]
            Export is read-only.
        .PARAMETER RootSquash
            Map access by root to the anonymous user.
        .PARAMETER AllSquash
            Map all access to the anonymous user.
        .PARAMETER AnonLocal
            The name of a local user to squash to.
        .EXAMPLE
            Modify-QQNFSExportHostAccess
                -ExportPath EXPORT_PATH -TenantID TENANT_ID | -ExportId EXPORT_ID
                -Position [POSITION]
                -HostRestriction [HOSTS]
                -ReadOnly $true
                -RootSquash
                -AllSquash
                -AnonLocal
        .LINK
    
        #>

    # CmdletBinding parameters.
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True,ParameterSetName = "ExportId")] [string]$ExportId,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [string]$ExportPath,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [int16]$TenantID,
        [Parameter(Mandatory = $True)] [string]$Position,
        [Parameter(Mandatory = $False)] [array]$HostRestrictions,
        [Parameter(Mandatory = $False)] [switch]$ReadOnly,
        [Parameter(Mandatory = $False)] [switch]$RootSquash,
        [Parameter(Mandatory = $False)] [switch]$AllSquash,
        [Parameter(Mandatory = $False)] [string]$AnonLocal

    )
    if ($SkipCertificateCheck -eq 'true') {
        $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
    }

    try {
        $foundShare = 1
        # Existing BearerToken check
        if (!$global:Credentials) {
            Login-QQCluster
        }
        else {
            if (!$global:Credentials.BearerToken.StartsWith("session-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"
        }

        # Multi-tenancy check
        $tenant_url = "/v1/multitenancy/tenants/"
        $response = Invoke-RestMethod -SkipCertificateCheck -Method 'GET' -Uri "https://${clusterName}:$portNumber$tenant_url" -Headers $TokenHeader -ContentType "application/json" -TimeoutSec 60 -ErrorAction:Stop
        $tenants = $response.entries
        Write-Debug ($response | ConvertTo-Json -Depth 10)

        # API url definition
        if ($tenants.Count -eq 1) {
            $url = "/v2/nfs/exports/"
        }
        else {
            $url = "/v3/nfs/exports/"
        }



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

            $nfsExports = $response.entries

            if ($ExportId) {
                foreach ($export in $nfsExports) {
                    if ($ExportId -eq $export.id) {
                        $ExportId = $export.id
                        $existingRestrictions = $export.restrictions
                        $url += $ExportId
                        $existingRestrictions = $export.restrictions
                        $i = 1
                        $newRestrictions = @()
                        foreach ($restriction in $existingRestrictions)
                        {

                            if ($i -eq $Position) {
                                $updatedRestriction = @{}

                                if ($HostRestrictions) {
                                    $updatedRestriction += @{
                                        "host_restrictions" = $HostRestrictions
                                    }
                                }
                                if ($RootSquash) {
                                    $updatedRestriction += @{
                                        "user_mapping" = "NFS_MAP_ROOT"
                                        "map_to_user" = @{
                                            "id_type" = "LOCAL_USER"
                                            "id_value" = $AnonLocal
                                        }
                                    }
                                }

                                if ($AllSquash) {
                                    $updatedRestriction += @{
                                        "user_mapping" = "NFS_MAP_ALL"
                                        "map_to_user" = @{
                                            "id_type" = "LOCAL_USER"
                                            "id_value" = $AnonLocal
                                        }
                                    }
                                }

                                if (-not ($AllSquash -or $RootSquash)) {
                                    $updatedRestriction += @{
                                        "user_mapping" = "NFS_MAP_NONE"
                                    }
                                }

                                if ($ReadOnly) {
                                    $updatedRestriction += @{
                                        "read_only" = $true
                                    }
                                }
                                else {
                                    $updatedRestriction += @{
                                        "read_only" = $false
                                    }
                                }


                                $updatedRestriction += @{
                                    "require_privileged_port" = $false
                                }

                                $newRestrictions += $updatedRestriction
                            }
                            else {
                                $newRestrictions += $restriction
                            }
                            $i = $i + 1
                        }
                    }
                }
            }
            elseif ($ExportPath) {
                # Response
                $nfsExports = $response.entries
                foreach ($export in $nfsExports) {
                    if (($ExportPath -eq $export.export_path) -and ($TenantID -eq $export.tenant_id)) {
                        $ExportId = $export.id
                        $existingRestrictions = $export.restrictions
                        $url += $ExportId
                        $i = 1
                        $newRestrictions = @()
                        foreach ($restriction in $existingRestrictions)
                        {
                            if ($i -eq $Position) {
                                $updatedRestriction = @{}

                                if ($HostRestrictions) {
                                    $updatedRestriction += @{
                                        "host_restrictions" = $HostRestrictions
                                    }
                                }
                                if ($RootSquash) {
                                    $updatedRestriction += @{
                                        "user_mapping" = "NFS_MAP_ROOT"
                                        "map_to_user" = @{
                                            "id_type" = "LOCAL_USER"
                                            "id_value" = $AnonLocal
                                        }
                                    }
                                }

                                if ($AllSquash) {
                                    $updatedRestriction += @{
                                        "user_mapping" = "NFS_MAP_ALL"
                                        "map_to_user" = @{
                                            "id_type" = "LOCAL_USER"
                                            "id_value" = $AnonLocal
                                        }
                                    }
                                }

                                if (-not ($AllSquash -or $RootSquash)) {
                                    $updatedRestriction += @{
                                        "user_mapping" = "NFS_MAP_NONE"
                                    }
                                }

                                if ($ReadOnly) {
                                    $updatedRestriction += @{
                                        "read_only" = $true
                                    }
                                }
                                else {
                                    $updatedRestriction += @{
                                        "read_only" = $false
                                    }
                                }


                                $updatedRestriction += @{
                                    "require_privileged_port" = $false
                                }

                                $newRestrictions += $updatedRestriction
                            }
                            else {
                                $newRestrictions += $restriction
                            }
                            $i = $i + 1
                        }
                        $foundShare = 1
                    }
                }

                if ($foundShare -eq 0) {
                    Write-Error "No matching export found. Check the export path and tenant id."
                    return
                }
            }
        }
        catch {
            $_.Exception.Response
        }


        # API Request body


        $body = @{
            "restrictions" = $newRestrictions
        }

        # 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 Remove-QQNFSExportHostAccess {
<#
        .SYNOPSIS
            Remove an access hosts are granted to an export
        .DESCRIPTION
            Remove the access hosts are granted to an export
        .PARAMETER ExportId [EXPORT_ID]
            The NFS export id
        .PARAMETER ExportPath [EXPORT_PATH]
            The NFS export path
        .PARAMETER TenantId [TENANT_ID]
            ID of the tenant the export is in. Only used if using the -ExportPath argument.
        .PARAMETER Position [POSITION]
            The position value of the host restriction that you can get List-QQNFSExportHostAccess
        .EXAMPLE
            Remove-QQNFSExportHostAccess
                -ExportPath EXPORT_PATH -TenantID TENANT_ID | -ExportId EXPORT_ID
                -Position [POSITION]
            .LINK
    
        #>

    # CmdletBinding parameters.
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $True,ParameterSetName = "ExportId")] [string]$ExportId,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [string]$ExportPath,
        [Parameter(Mandatory = $True,ParameterSetName = "ExportPath")] [int16]$TenantID,
        [Parameter(Mandatory = $True)] [string]$Position

    )
    if ($SkipCertificateCheck -eq 'true') {
        $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
    }

    try {
        $foundShare = 1
        # Existing BearerToken check
        if (!$global:Credentials) {
            Login-QQCluster
        }
        else {
            if (!$global:Credentials.BearerToken.StartsWith("session-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/nfs/exports/"


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

            $nfsExports = $response.entries

            if ($ExportId) {
                foreach ($export in $nfsExports) {
                    if ($ExportId -eq $export.id) {
                        $ExportId = $export.id
                        $existingRestrictions = $export.restrictions
                        $url += $ExportId
                        $existingRestrictions = $export.restrictions
                        $i = 1
                        $newRestrictions = @()
                        foreach ($restriction in $existingRestrictions)
                        {

                            if ($i -ne $Position) {
                                $newRestrictions += $restriction
                            }
                            $i = $i + 1
                        }
                    }
                }
            }
            elseif ($ExportPath) {
                # Response
                $nfsExports = $response.entries
                foreach ($export in $nfsExports) {
                    if (($ExportPath -eq $export.export_path) -and ($TenantID -eq $export.tenant_id)) {
                        $ExportId = $export.id
                        $existingRestrictions = $export.restrictions
                        $url += $ExportId
                        $i = 1
                        $newRestrictions = @()
                        foreach ($restriction in $existingRestrictions)
                        {

                            if ($i -ne $Position) {
                                $newRestrictions += $restriction
                            }
                            $i = $i + 1
                        }
                        $foundShare = 1
                    }
                }

                if ($foundShare -eq 0) {
                    Write-Error "No matching export found. Check the export path and tenant id."
                    return
                }
            }
        }
        catch {
            $_.Exception.Response
        }


        # API Request body


        $body = @{
            "restrictions" = $newRestrictions
        }

        # 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 Get-QQNFSSettings {
<#
    .SYNOPSIS
        Get NFS settings
    .DESCRIPTION
        Get NFS settings
    .EXAMPLE
        Get-QQNFSSettings [-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.StartsWith("session-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 = "/v2/nfs/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-QQNFSSettings {
<#
    .SYNOPSIS
        Set NFS server settings
    .DESCRIPTION
        Partially set settings for the NFS Server.
    .PARAMETER V4Enabled
        Enables mounting with the NFSv4.1 protocol
    .PARAMETER V4Disabled
        Disables mounting with the NFSv4.1 protocol
    .PARAMETER Krb5Enabled
        Enables mounting with KRB5 security
    .PARAMETER Krb5Disabled
        Disables mounting with KRB5 security
    .PARAMETER Krb5PEnabled
        Enables mounting with KRB5p security
    .PARAMETER Krb5PEnabled
        Disables mounting with KRB5p security
    .PARAMETER Krb5iEnabled
        Enables mounting with KRB5i security
    .PARAMETER Krb5iEnabled
        Disables mounting with KRB5p security
    .PARAMETER AuthSysEnabled
        Enables mounting with AUTH_SYS security
    .PARAMETER AuthSysEnabled
        Disables mounting with AUTH_SYS security
    .EXAMPLE
        Modify-QQNFSSettings [-Json]
            -V4Enabled
            -V4Disabled
            -Krb5Enabled
            -Krb5Disabled
            -Krb5pEnabled
            -Krb5pDisabled
            -Krb5iEnabled
            -Krb5iDisabled
            -AuthSysEnabled
            -AuthSysDisabled
        .LINK

    #>

    # CmdletBinding parameters.
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $False)] [switch]$Json,
        [Parameter(Mandatory = $False)] [switch]$V4Enabled,
        [Parameter(Mandatory = $False)] [switch]$Krb5Enabled,
        [Parameter(Mandatory = $False)] [switch]$Krb5pEnabled,
        [Parameter(Mandatory = $False)] [switch]$Krb5iEnabled,
        [Parameter(Mandatory = $False)] [switch]$AuthSysEnabled,
        [Parameter(Mandatory = $False)] [switch]$V4Disabled,
        [Parameter(Mandatory = $False)] [switch]$Krb5Disabled,
        [Parameter(Mandatory = $False)] [switch]$Krb5pDisabled,
        [Parameter(Mandatory = $False)] [switch]$Krb5iDisabled,
        [Parameter(Mandatory = $False)] [switch]$AuthSysDisabled
    )
    if ($SkipCertificateCheck -eq 'true') {
        $PSDefaultParameterValues = @("Invoke-RestMethod:SkipCertificateCheck",$true)
    }

    try {
        # Existing BearerToken check
        if (!$global:Credentials) {
            Login-QQCluster
        }
        else {
            if (!$global:Credentials.BearerToken.StartsWith("session-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 ($V4Enabled) {
            $body += @{
                "v4_enabled" = $true
            }
        }
        elseif ($V4Disabled) {
            $body += @{
                "v4_enabled" = $false
            }
        }

        if ($Krb5Enabled) {
            $body += @{
                "krb5_enabled" = $true
            }
        }
        elseif ($Krb5Disabled) {
            $body += @{
                "krb5_enabled" = $false
            }
        }

        if ($Krb5pEnabled) {
            $body += @{
                "krb5p_enabled" = $true
            }
        }
        elseif ($Krb5pDisabled) {
            $body += @{
                "krb5p_enabled" = $false
            }
        }

        if ($Krb5iEnabled) {
            $body += @{
                "krb5i_enabled" = $true
            }
        }
        elseif ($Krb5iDisabled) {
            $body += @{
                "krb5i_enabled" = $false
            }
        }

        if ($AuthSysEnabled) {
            $body += @{
                "auth_sys_enabled" = $true
            }
        }
        elseif ($AuthSysDisabled) {
            $body += @{
                "auth_sys_enabled" = $false
            }
        }




        # API url definition
        $url = "/v2/nfs/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
    }
}