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 -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/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 [EXPORT_PATH] -TenantID [TENANT_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)] [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 -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/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 -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/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] -TenantID [TENANT_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 -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/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 -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/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 -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/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 -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/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 -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"
            }
    
            # 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
    
                if ($url -like '/v2/*') {
                                       $nfsExports = $response
                                } else {
                                       $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 -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/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 -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 = "/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 -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 ($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
        }
    }