Configure TrueNAS with Powershell and API

We needed to test TrueNAS and decided to use Powershell and TrueNAS API on two VMware VMs.

Tested on:
Windows 11 Pro v22H2 (OS Build 22621.3593)
Powershell 5.1.22621.2506
TrueNAS Scale Dragonfish-24.04.1 on VMware

Here’s the script if anyone wants to check it out.


<#
Purpose: 
    Test provisioning TrueNAS with Powershell/TrueNAS API


Reasoning: 
    If SMB is needed, Windows is used in the environment and powershell is available


Tested on: 
    Windows 11 Pro v22H2 (OS Build 22621.3593)
    Powershell 5.1.22621.2506 
    TrueNAS Scale Dragonfish-24.04.1 on VMware


Overview 
    Setup two TrueNAS Scale systems with 1-way PUSH replication 


Details
    Configure TrueNAS Scale "TN1"
        Set DNS,Hostname, Domain name, Netbios name, NTP
        Create RAID Z1 Pool with all available disks
        Create Datset for SMB share
        SMB Share / SBM Share Group and User / Set SMB Service to autostart on reboots
        Enable snapshots
        Enable PUSH replication to TN2  (after configuring TN2)
    Configure TrueNAS Scale "TN2" with
        Set DNS,Hostname, Domain name, Netbios name, NTP
        Create RAID Z1 Pool with all available disks


Your responsibility
    Do not test in production
    Change passwords and apikeys after testing
    Better yet, destory the test TrueNAS systems and re-use anything of value from this example


How to get started

    Prerequisites
    1. Two systems with fresh install of TrueNAS Scale  (VM or baremetal)
        installing TrueNAS is outside the scope   
        TrueNAS Scale download: https://www.TrueNAS.com/download-truenas-scale/     
    2. IP address of each is pingable from where powershell will run
    3. Each system needs 3 or more unused drives to create the pool
    4. Script will be run in Powershell ISE


    Steps to configure TrueNAS Scale 
    1. Create the apikey on each system
        a. Log into the first TrueNAS SCALE system as admin using your browser
        b. In the top right corner, click "admin", then click "API Keys"
        c. Click "Add", type "admin_apikey", click "Save"
        d. Click "Copy To Clipboard" to copy the newly created "API Key" and save it somewhere, we will need it.
           Note: if you lose it API Key, deleted it and re-create it
        e: do the same thing for the second TrueNAS Scale system 

    2. Set the ip address and apikey for each TrueNAS Scale system
        a. search for $Script:TN1_name   set the ip address for the first TrueNAS Scale system
        b. search for $Script:TN1_apikey set the apikey for the first TrueNAS Scale system
        c. search for $Script:TN2_name   set the ip address for the second TrueNAS Scale system
        d. search for $Script:TN2_apikey set the apikey for the second TrueNAS Scale system
        e: review the rest of the variables but only modify if you are confident in the changes
        f: note: in the real world you will encrypt the API keys, for testing they are clear text
    
    3. Save the changes and run the entire script in Powershell ISE to load variables and functions

    4. Test API Access
        highlight/select "BasicInfo -TN tn1" below and "Run Selection" or hit F8
        This should return several lines and no error
        you can also copy "BasicInfo -TN tn1", paste in the console and hit enter  (no quotes when copying)
        do the same with "BasicInfo -TN tn2"
        
        BasicInfo -TN tn1
        BasicInfo -TN tn2

        If you receive errors, do not proceed since API access is not working. Troubleshooting needed.

    5. Configure first TrueNAS Scale system
        Higilight/select each line below and run with F8 or copy/paste in the console and hit enter
        Suggestion: do one line at a time instead of all at once

        SetDNS -TN tn1
        SetHostName -TN tn1
        SetDomainName -TN tn1
        UpdateSmbNetbios -TN tn1
        SetNtpServers -TN tn1
        SetSnmpService -TN tn1

        CreateZ1Pool -TN tn1
        CreateGroupAndUserForSmb -TN tn1
        CreateDatasetForSmbShare -TN tn1
        CreateSmbShare -TN tn1
        SetSmbAcl -TN tn1
        SetSmbServiceAutostart -TN tn1

        EnabledSmbSnapshots-every5min-keep24hours -TN tn1
        EnabledSmbSnapshots-every8hours-keep7days -TN tn1
        ShowSnapshotTasks -TN tn1

        DisableHttpRedirect -TN tn1
        SetupSShConnection -TN tn1
        ListAllKeychainCredentials -tn "tn1"
        CreateReplicationTasksForSnapshots -sourceTN "tn1" -targetTN "tn2"  
        #EnableHttpRedirect -TN tn1
        #target for replicaiton needs to be configured below
        

    6. Configure second TrueNAS Scale system
        Higilight/select each line below and run with F8 or copy/paste in the console and hit enter
        Suggestion: do one line at a time instead of all at once
        
        SetDNS -TN tn2
        SetHostName -TN tn2
        SetDomainName -TN tn2
        UpdateSmbNetbios -TN tn2
        SetNtpServers -TN tn2
        SetSnmpService -TN tn2
        CreateZ1Pool -TN tn2

        
        ##########
        # -> wait 5 minutes for replication from tn1 to create the dataset repl-WinShare1 on tn2
        # -> URL: http://{tn2 ip address}/ui/datasets/test-z1-pool
        ##########
        CreateGroupAndUserForSmb -TN tn2
        CreateSmbShare -TN tn2
        #SetSmbAcl -TN tn2
        SetSmbServiceAutostart -TN tn2


    7. Access the shares
        Username: will
        Password: password
        truenas1: \\{ip of truenas system 1}\winshare1
        truenas1: \\{ip of truenas system 2}\repl-winshare2


#> 


<#
https://{ip of TrueNAS}/ui/system/general -> Gui -> Web Interface HTTP -> HTTPS Redirect: Disabled 
#>



##########################
#### script variables ####
##########################

$Script:TN1_name     = "0.0.0.0" # change to first TrueNAS scale ip or dns name
$Script:TN1_apikey   = "" # change to apikey of first TrueNAS scale
$Script:TN1_hostname = "TEST-TN-01"


$Script:TN2_name     = "0.0.0.0" # change to second TrueNAS scale ip or dns name
$Script:TN2_apikey   = "" # change to apikey of second TrueNAS scale
$Script:TN2_hostname = "TEST-TN-02"


$Script:TN1_DNS1 = "8.8.8.8"  #Google Primary DNS
$Script:TN1_DNS2 = "8.8.4.4"  #Google Secondary
$Script:TN1_DNS3 = "9.9.9.9"  #Quad9 Primary
$Script:TN2_DNS1 = "8.8.8.8"  #Google Primary DNS
$Script:TN2_DNS2 = "8.8.4.4"  #Google Secondary
$Script:TN2_DNS3 = "9.9.9.9"  #Quad9 Primary


$Script:TN1_NTP1 = "0.debian.pool.ntp.org"  #TrueNAS NTP Default
$Script:TN1_NTP2 = "1.debian.pool.ntp.org"  #TrueNAS NTP Default
$Script:TN1_NTP3 = "2.debian.pool.ntp.org"  #TrueNAS NTP Default
$Script:TN2_NTP1 = "0.debian.pool.ntp.org"  #TrueNAS NTP Default
$Script:TN2_NTP2 = "1.debian.pool.ntp.org"  #TrueNAS NTP Default
$Script:TN2_NTP3 = "2.debian.pool.ntp.org"  #TrueNAS NTP Default

$Script:SnmpRo = "public" #readonly snmp community name
$Script:SnmpEnable = $true


$Script:ReplicationPrependText  = "repl-"
$Script:TN1_ReplicationAccount  = $Script:TN1_hostname + "_Replication"
$Script:TN1_ReplicationPassword = "password"
$Script:TN2_ReplicationAccount  = $Script:TN2_hostname + "_Replication"
$Script:TN2_ReplicationPassword = "password"


$Script:TN1_WindowsShareName          = "WinShare1"
$Script:TN1_WindowsShareGroup         = "WinShare1"
$Script:TN1_WindowsShareReadOnly      = $false
$Script:TN2_WindowsShareName          = $ReplicationPrependText + "WinShare1"
$Script:TN2_WindowsShareGroup         = $ReplicationPrependText + "WinShare1"
$Script:TN2_WindowsShareReadOnly      = $true

#keeping usernames and password consistent
$Script:WindowsShareUser1Username = "Will"
$Script:WindowsShareUser1FullName = "Will Neverknow"
$Script:WindowsShareUser1Password = "password"  #for testing only



$Script:DomainName = "contoso.com"
$Script:PoolName = "test-z1-pool"
$Script:RaidType = "RAIDZ1"
$Script:JsonColor = "Cyan"

$Script:ShowUrlInfo = $true  #show URL/Method/Json
$Script:BarLength   = 80      #number of characters in separation bar
$Script:BarChar     = "V"     #character for separation bar
$Script:BarColor    = "Gray"






Function Invoke-TrueNASAPI {
  param (
    [string] $tn,
    [string] $method,
    [string] $url,
    [object] $body = $null

  )

    if ($tn -eq $null) {Write-Output "TN parameter missing. Exiting."; Start-Sleep -Seconds 5 ;break}

    $apiKey = Get-Variable -Name "${TN}_apikey" -ValueOnly
    $apiUrltemp = Get-Variable -Name "${TN}_name" -ValueOnly
    $apiUrl = "https://" + $apiUrltemp + "/api/v2.0"

    $headers = New-Object System.Net.WebHeaderCollection
    $headers = @{
        accept="*/*"
        Authorization="Bearer $apiKey"
    }

 
add-type @"
   using System.Net;
   using System.Security.Cryptography.X509Certificates;
   public class TrustAllCertsPolicy : ICertificatePolicy {
      public bool CheckValidationResult(
      ServicePoint srvPoint, X509Certificate certificate,
      WebRequest request, int certificateProblem) {
      return true;
   }
}
"@
  [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
  [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

  $contentType = "application/json"
  
  
    if ($ShowUrlInfo) {
        Write-Host $($BarChar * $BarLength) -ForegroundColor $BarColor
        Write-Host "URL: $apiUrl/$url" -ForegroundColor $JsonColor
        Write-Host "Method: $method" -ForegroundColor $JsonColor
        if ($body -ne $null) {
            Write-Host "JSON" -ForegroundColor $JsonColor
            Write-Host "$body" -ForegroundColor $JsonColor
        }
    }
  
  
  #where the magic happens
    $result = Invoke-RestMethod -Uri "$apiUrl/$url" -Method $method -Headers $headers -UseBasicParsing -ContentType $contentType -Body $body
    $result



}


Function BasicInfo{
    param ([string] $tn)

    Invoke-TrueNASAPI -TN $tn -Method Get -Url "system/info" 
    
    $apiUrltemp = Get-Variable -Name "${TN}_name" -ValueOnly
    $apiUrl = "http://" + $apiUrltemp + "/api/docs/"
    Write-Host "Local API URL [$apiUrl] -> Click RESTful 2.0" -ForegroundColor $JsonColor
    write-host "TrueNAS RESTful API [https://www.truenas.com/docs/api/scale_rest_api.html]"

}

Function GetInfo{
    param ([string] $tn)

    Invoke-TrueNASAPI -TN $tn -Method Get -Url "system/info" 
    Invoke-TrueNASAPI -TN $tn -Method Get -Url "system/hostname" 
    Invoke-TrueNASAPI -TN $tn -Method Get -Url "network/configuration"

    # Get information on all disks
    Invoke-TrueNASAPI -TN $tn -Method Get -Url "disk" #| Format-Table -AutoSize

    # Get unused disks
    Invoke-TrueNASAPI -TN $tn -Method Post -Url "disk/get_unused"

    # Get information on all pools
    Invoke-TrueNASAPI -TN $tn -Method Get -Url "pool"

    Invoke-TrueNASAPI -TN $tn -Method Get -Url "system/ntpserver" #| Format-Table -AutoSize
    Invoke-TrueNASAPI -TN $tn -Method Get -Url "boot/get_disks"
}

Function SetDns {
    param ([string] $tn)

    $DNS1 = Get-Variable -Name "${TN}_DNS1" -ValueOnly
    $DNS2 = Get-Variable -Name "${TN}_DNS2" -ValueOnly
    $DNS3 = Get-Variable -Name "${TN}_DNS3" -ValueOnly

    $GetNetworkConfig = Invoke-TrueNASAPI -TN $tn -Method Get -Url "network/configuration"
    $CurrentDns1 = $GetNetworkConfig.nameserver1
    $CurrentDns2 = $GetNetworkConfig.nameserver2
    $CurrentDns3 = $GetNetworkConfig.nameserver3

    Write-Output "Current DNS Settings:"
    Write-Output "nameserver1 [$CurrentDns1]"
    Write-Output "nameserver2 [$CurrentDns2]"
    Write-Output "nameserver3 [$CurrentDns3]"

    # Only update DNS if it is different from the desired state
    if ($CurrentDns1 -ne $DNS1 -or $CurrentDns2 -ne $DNS2 -or $CurrentDns3 -ne $DNS3) {
        # Define the DNS properties
        $DnsProperties = @{
            nameserver1 = $DNS1
            nameserver2 = $DNS2
            nameserver3 = $DNS3
        }
        $DnsPropertiesJson = $DnsProperties | ConvertTo-Json

        $response = Invoke-TrueNASAPI -TN $tn -Method PUT -Url "network/configuration" -body $DnsPropertiesJson

        # Check response
        if ($response) {
            $UpdatedDNS = Invoke-TrueNASAPI -TN $tn -Method Get -Url "network/configuration"
            $UpdatedDns1 = $UpdatedDNS.nameserver1
            $UpdatedDns2 = $UpdatedDNS.nameserver2
            $UpdatedDns3 = $UpdatedDNS.nameserver3
            Write-Output "DNS updated to:"
            Write-Output "nameserver1 [$UpdatedDns1]"
            Write-Output "nameserver2 [$UpdatedDns2]"
            Write-Output "nameserver3 [$UpdatedDns3]"
        } else {
            Write-Output "Error updating DNS: $($response | ConvertTo-Json -Depth 100)"
        }
    } else {
        Write-Output "DNS is already set to the desired values. No update needed."
    }
}

Function SetHostName {
    param ([string] $tn)

    $NewHostname = Get-Variable -Name "${TN}_hostname" -ValueOnly
    $CurrentHostname = Invoke-TrueNASAPI -TN $tn -Method Get -Url "system/hostname"

    # Only update hostname if it is different from the desired state
    if ($CurrentHostname -ne $NewHostname) {
        $NewHostnameData = '{ "hostname": ' + '"' + $NewHostname + '" }'
        $response = Invoke-TrueNASAPI -TN $tn -Method PUT -Url "network/configuration" -body $NewHostnameData

        # Check response
        if ($response) {
            $UpdatedHostname = Invoke-TrueNASAPI -TN $tn -Method Get -Url "system/hostname"
            Write-Output "Updated Hostname [$UpdatedHostname]"
        } else {
            Write-Output "Error updating hostname: $($response | ConvertTo-Json -Depth 100)"
        }
    } else {
        Write-Output "Hostname is already set to [$NewHostname]. No update needed."
    }
}

Function SetDomainName {
    param ([string] $tn)

    $DomainName = Get-Variable -Name "DomainName" -ValueOnly

    # Get the current domain name
    $GetNetworkConfig = Invoke-TrueNASAPI -TN $tn -Method Get -Url "network/configuration"
    $CurrentDomainName = $GetNetworkConfig.domain

    # Only update the domain name if it is different from the desired state
    if ($CurrentDomainName -ne $DomainName) {
        # Construct the request body
        $DomainNameBody = @{
            "domain" = $DomainName
        }

        $DomainNameJson = $DomainNameBody | ConvertTo-Json 

        # Call the TrueNAS API to set the domain name
        $response = Invoke-TrueNASAPI -TN $tn -Method Put -Url "network/configuration" -Body $DomainNameJson

        if ($response) {
            $UpdatedNetworkConfig = Invoke-TrueNASAPI -TN $tn -Method Get -Url "network/configuration"
            $UpdatedDomainName = $UpdatedNetworkConfig.domain
            Write-Host "Domain name has been updated to [$UpdatedDomainName]"
        } else {
            Write-Host "Failed to set the domain name: $($response | ConvertTo-Json -Depth 100)"
        }
    } else {
        Write-Output "Domain name is already set to [$DomainName]. No update needed."
    }
}

Function SetNtpServers {
    param ([string] $tn)

    # Retrieve the desired NTP servers from variables
    $NTP1 = Get-Variable -Name "${TN}_NTP1" -ValueOnly
    $NTP2 = Get-Variable -Name "${TN}_NTP2" -ValueOnly
    $NTP3 = Get-Variable -Name "${TN}_NTP3" -ValueOnly

    # Retrieve the current NTP server configuration
    $GetNtpConfig = Invoke-TrueNASAPI -TN $tn -Method Get -Url "system/ntpserver"
    $CurrentNtpServers = $GetNtpConfig | Select-Object -ExpandProperty address

    Write-Output "Current NTP Servers:"
    $CurrentNtpServers | ForEach-Object { Write-Output "NTP Server [$($_)]" }

    # Only update NTP servers if they are different from the desired state
    $desiredNtpServers = @($NTP1, $NTP2, $NTP3)
    $serversToAdd = $desiredNtpServers | Where-Object { $_ -notin $CurrentNtpServers }
    $serversToRemove = $CurrentNtpServers | Where-Object { $_ -notin $desiredNtpServers }

    if ($serversToAdd.Count -eq 0 -and $serversToRemove.Count -eq 0) {
        Write-Output "NTP servers are already set to the desired values. No update needed."
    } else {
        # Remove old NTP servers
        foreach ($server in $serversToRemove) {
            $ntpServerId = ($GetNtpConfig | Where-Object { $_.address -eq $server }).id
            Invoke-TrueNASAPI -TN $tn -Method Delete -Url "system/ntpserver/$ntpServerId"
            Write-Host "Removed NTP server: $server"
        }

        # Add new NTP servers
        foreach ($server in $serversToAdd) {
            $ntpServer = @{
                address = $server
                burst = $false
                iburst = $true
                prefer = $false
            }
            $ntpServerJson = $ntpServer | ConvertTo-Json
            Invoke-TrueNASAPI -TN $tn -Method Post -Url "system/ntpserver" -Body $ntpServerJson
            Write-Host "Added NTP server: $server"
        }

        Write-Host "NTP servers have been updated successfully."
    }
}

Function CreateZ1Pool {
    param ([string] $tn)

    # Check if the pool already exists
    $existingPools = Invoke-TrueNASAPI -TN $tn -Method Get -Url "pool"
    $existingPool = $existingPools | Where-Object { $_.name -eq $PoolName }

    if ($existingPool) {
        Write-Output "Pool [$PoolName] already exists. No action needed."
        return
    }

    $unusedDisks = Invoke-TrueNASAPI -TN $tn -Method Post -Url "disk/get_unused"
    $bCreateZ1Pool = $false

    # Check for unused disks
    if ($unusedDisks.Count -eq 0) {
        Write-Host "No unused disks found!"
        return
    }

    Write-Host "[$($unusedDisks.Count)] unused disks found!"
    $unusedDisks.devname | ForEach-Object { Write-Host "[$_]" }

    $UnusedDiskDevs = $unusedDisks | Where-Object { $_.serial -ne $null -and $_.serial -ne "" } | Select-Object -ExpandProperty devname

    if ($UnusedDiskDevs.Count -lt 3) {
        Write-Host "Less than 3 disks with valid serial numbers found! Pool creation requires at least 3 disks."
        return
    }

    Write-Host "[$($UnusedDiskDevs.Count)] disks with valid serial numbers found!"
    $bCreateZ1Pool = $true

    if ($bCreateZ1Pool) {
        $zpoolConfig = @{
            "name" = $PoolName
            "encryption" = $false
            "deduplication" = $null
            "checksum" = $null
            "topology" = @{
                "data" = @(
                    @{
                        "disks" = $UnusedDiskDevs
                        "type"  = $RaidType
                    }
                )
            }
            "allow_duplicate_serials" = $false
        }

        $zpoolConfigJson = $zpoolConfig | ConvertTo-Json -Depth 100

        # Create ZFS pool (RAID-Z1)
        $createPoolResponseID = Invoke-TrueNASAPI -TN $tn -Method Post -Url "pool" -Body $zpoolConfigJson
        $jobUrl = "core/get_jobs/?id=" + $createPoolResponseID

        do {
            $GetState = Invoke-TrueNASAPI -TN $tn -Method Get -Url $jobUrl
            $GetState
            if ($GetState.State -eq "RUNNING") {
                Write-Output "Please wait..."
                Start-Sleep -Seconds 10
            }
        } while ($GetState.State -eq "RUNNING")
    }
}

Function CreateGroupAndUserForSmb {
    param ([string] $tn)


    $WindowsShareGroup = Get-Variable -Name "${TN}_WindowsShareGroup" -ValueOnly

    # Check if the group already exists
    $existingGroups = Invoke-TrueNASAPI -TN $tn -Method Get -Url "group"
    $existingGroup = $existingGroups | Where-Object { $_.group -eq $WindowsShareGroup }

    if ($existingGroup) {
        Write-Output "Group [$WindowsShareGroup] already exists. No action needed."
        $groupId = $existingGroup.id
    } else {
        # Define the group properties
        $groupProperties = @{
            name = $WindowsShareGroup 
            smb = $true
            allow_duplicate_gid = $false
        }
        $groupPropertiesJson = $groupProperties | ConvertTo-Json

        # Create the group
        $createGroupResponse = Invoke-TrueNASAPI -TN $tn -Method Post -Url "group" -body $groupPropertiesJson
        $groupId = $createGroupResponse

        # Debugging step: Output the entire group creation response
        Write-Host "Group creation response: $groupId"
    }

    # Debugging step: Output the group ID
    Write-Host "Group ID: $groupId"

    # Check if the user already exists
    $existingUsers = Invoke-TrueNASAPI -TN $tn -Method Get -Url "user"
    $existingUser = $existingUsers | Where-Object { $_.username -eq $WindowsShareUser1Username }

    if ($existingUser) {
        Write-Output "User [$WindowsShareUser1Username] already exists. No action needed."
    } else {
        # Define the user properties
        $userProperties = @{
            username = $WindowsShareUser1Username
            full_name = $WindowsShareUser1FullName
            password = $WindowsShareUser1Password
            smb = $true
            group = $groupId
        }
        $userPropertiesJson = $userProperties | ConvertTo-Json

        # Debugging step: Output the user creation JSON
        Write-Host "User creation JSON: $userPropertiesJson"

        # Create the user
        $createUserResponse = Invoke-TrueNASAPI -TN $tn -Method Post -Url "user" -body $userPropertiesJson
        Write-Host "User created id [$createUserResponse]"
    }
}

Function CreateDatasetForSmbShare {
    param ([string] $tn)


    $WindowsShareName = Get-Variable -Name "${TN}_WindowsShareName" -ValueOnly

    # Define dataset parameters
    $datasetName = $WindowsShareName 
    $SmbShareName = $datasetName
    $datasetPath = "$poolName/$datasetName"
    $datasetMntPath = "/mnt/$poolName/$datasetName"

    # Check if the dataset already exists
    $existingDatasets = Invoke-TrueNASAPI -TN $tn -Method Get -Url "pool/dataset"
    $datasetExists = $existingDatasets | Where-Object { $_.name -eq $datasetPath }

    if ($datasetExists) {
        Write-Host "Dataset [$datasetName] already exists. No action needed."
    } else {
        # Create SMB dataset
        $datasetBody = @{
            "name" = $datasetPath
            "type" = "FILESYSTEM"
            "comments" = "Dataset for $datasetName"
            "casesensitivity" = "INSENSITIVE"
            "aclmode" = "RESTRICTED"
            "share_type" = "SMB"
        }

        $datasetBodyJson = $datasetBody | ConvertTo-Json 

        $createDatasetResponse = Invoke-TrueNASAPI -TN $tn -Method Post -Url "pool/dataset" -body $datasetBodyJson

        # Check if dataset creation was successful
        if ($createDatasetResponse.name -eq $datasetPath) {
            Write-Host "Dataset [$datasetName] created successfully."
        } else {
            Write-Host "Failed to create dataset [$datasetName]."
        }
    }
}

Function CreateSmbShare {
    param ([string] $tn)


    $WindowsShareName     = Get-Variable -Name "${TN}_WindowsShareName" -ValueOnly
    $WindowsShareReadOnly = Get-Variable -Name "${TN}_WindowsShareReadOnly" -ValueOnly

    # Define dataset parameters
    $datasetName = $WindowsShareName 
    $SmbShareName = $datasetName
    $PoolName = $PoolName  # also defined elsewhere when creating dataset
    $datasetPath = "$poolName/$datasetName"
    $datasetMntPath = "/mnt/$poolName/$datasetName"

    # Check if the SMB share already exists
    $existingSmbShares = Invoke-TrueNASAPI -TN $tn -Method Get -Url "sharing/smb"
    $smbShareExists = $existingSmbShares | Where-Object { $_.name -eq $SmbShareName }

    if ($smbShareExists) {
        Write-Host "SMB Share [$SmbShareName] already exists. No action needed."
    } else {
        # Define the SMB share properties
        $smbShareProperties = @{
            name = $SmbShareName
            path = $datasetMntPath  # Specify the path to the dataset
            ro = $WindowsShareReadOnly  # set read-only access
            guestok = $false  # Disable guest access
            acl = $true  # Enable support for storing the SMB Security Descriptor as a Filesystem ACL
            streams = $true  # Enable support for storing alternate datastreams as filesystem extended attributes
            fsrvp = $true  # Enable support for the filesystem remote VSS protocol
            shadowcopy = $true  # Enable support for the volume shadow copy service
            audit = @{
                enable = $true  # Enable SMB share auditing
                watch_list = @()  # Specify a list of groups for which to generate audit messages (defaults to all groups)
                ignore_list = @()  # Specify a list of groups to ignore when auditing
            }
        }

        $smbSharePropertiesJson = $smbShareProperties | ConvertTo-Json 

        # Create the SMB share
        $createSmbShareResponse = Invoke-TrueNASAPI -TN $tn -Method Post -Url "sharing/smb" -body $smbSharePropertiesJson

        # Check if SMB share creation was successful
        if ($createSmbShareResponse.name -eq $SmbShareName) {
            Write-Host "SMB Share [$SmbShareName] created successfully."
        } else {
            Write-Host "Failed to create SMB Share [$SmbShareName]."
        }
    }
}

Function UpdateSmbNetbios {
    param ([string] $tn)

    $Hostname = Get-Variable -Name "${TN}_hostname" -ValueOnly

    if ($Hostname.Length -gt 15) {
        $Hostname = $Hostname.Substring(0, 15)
    }

    # Retrieve current SMB configuration
    $currentSmbConfig = Invoke-TrueNASAPI -TN $tn -Method Get -Url "smb"
    $currentNetbiosName = $currentSmbConfig.netbiosname

    # Only update NetBIOS name if it is different from the desired state
    if ($currentNetbiosName -ne $Hostname) {
        # Define the SMB share properties
        $smbNetbiosProperties = @{
            netbiosname = $Hostname
        }

        $smbNetbiosPropertiesJson = $smbNetbiosProperties | ConvertTo-Json

        # Update the SMB configuration
        $updateSmbNetbiosResponse = Invoke-TrueNASAPI -TN $tn -Method Put -Url "smb" -body $smbNetbiosPropertiesJson

        # Check response
        if ($updateSmbNetbiosResponse) {
            $updatedSmbConfig = Invoke-TrueNASAPI -TN $tn -Method Get -Url "smb"
            $updatedNetbiosName = $updatedSmbConfig.netbiosname
            Write-Output "NetBIOS name has been updated to [$updatedNetbiosName]"
        } else {
            Write-Output "Failed to update NetBIOS name: $($updateSmbNetbiosResponse | ConvertTo-Json -Depth 100)"
        }
    } else {
        Write-Output "NetBIOS name is already set to [$Hostname]. No update needed."
    }
}

Function EnabledSmbSnapshots-every5min-keep24hours {
    param ([string] $tn)

    $WindowsShareName = Get-Variable -Name "${TN}_WindowsShareName" -ValueOnly
    
    # Define variables
    $datasetName = $WindowsShareName 
    $datasetPath = "$poolName/$datasetName"

    # Define the desired snapshot task payload
    $snapshotTask = @{
        naming_schema = "%Y_%m_%d_%H_%M_5Min_Snapshot_Task" # Adjusted naming schema
        recursive = $false
        lifetime_value = 24
        lifetime_unit = "HOUR"
        schedule = @{
            minute = "0,5,10,15,20,25,30,35,40,45,50,55"
            hour = "*"
            dom = "*"
            month = "*"
            dow = "*"
        }
        enabled = $true
        dataset = $datasetPath
    }  

    $snapshotTaskPayload = $snapshotTask | ConvertTo-Json
        
    # Get existing snapshot tasks
    $existingSnapshotTasks = Invoke-TrueNASAPI -TN $tn -Method Get -Url "pool/snapshottask"

    # Check if the desired snapshot task already exists
    $taskExists = $false
    $taskId = $null
    foreach ($task in $existingSnapshotTasks) {
        if ($task.dataset -eq $datasetPath -and $task.naming_schema -eq $snapshotTask.naming_schema) {
            $taskExists = $true
            $taskId = $task.id
            # Check if the configuration is the same
            if ($task.recursive -eq $snapshotTask.recursive -and 
                $task.lifetime_value -eq $snapshotTask.lifetime_value -and 
                $task.lifetime_unit -eq $snapshotTask.lifetime_unit -and 
                $task.schedule.minute -eq $snapshotTask.schedule.minute -and
                $task.schedule.hour -eq $snapshotTask.schedule.hour -and
                $task.schedule.dom -eq $snapshotTask.schedule.dom -and
                $task.schedule.month -eq $snapshotTask.schedule.month -and
                $task.schedule.dow -eq $snapshotTask.schedule.dow -and
                $task.enabled -eq $snapshotTask.enabled) {
                Write-Host "Snapshot task for dataset [$datasetPath] with the naming schema [$($snapshotTask.naming_schema)] and the desired configuration already exists. No action needed."
                return
            } else {
                Write-Host "Snapshot task for dataset [$datasetPath] with the naming schema [$($snapshotTask.naming_schema)] exists but with a different configuration. Updating..."
                # Update the existing snapshot task
                $updateSnapshotTaskPayload = @{
                    naming_schema = $snapshotTask.naming_schema
                    recursive = $snapshotTask.recursive
                    lifetime_value = $snapshotTask.lifetime_value
                    lifetime_unit = $snapshotTask.lifetime_unit
                    schedule = $snapshotTask.schedule
                    enabled = $snapshotTask.enabled
                    dataset = $snapshotTask.dataset
                } | ConvertTo-Json
                $updateUrl = "pool/snapshottask/id/$taskId"
                Write-Output "Updating snapshot task at URL: https://$($tn)/api/v2.0/$updateUrl"
                $updateResponse = Invoke-TrueNASAPI -TN $tn -Method Put -Url $updateUrl -body $updateSnapshotTaskPayload
                if ($updateResponse) {
                    Write-Host "Snapshot task for dataset [$datasetPath] with the naming schema [$($snapshotTask.naming_schema)] updated successfully."
                } else {
                    Write-Host "Failed to update the snapshot task."
                }
                return
            }
        }
    }

    # Create the periodic snapshot task if it does not exist
    if (-not $taskExists) {
        $createResponse = Invoke-TrueNASAPI -TN $tn -Method Post -Url "pool/snapshottask" -body $snapshotTaskPayload
        if ($createResponse) {
            Write-Host "Snapshot task for dataset [$datasetPath] with the naming schema [$($snapshotTask.naming_schema)] created successfully."
        } else {
            Write-Host "Failed to create the snapshot task."
        }
    }
}

Function EnabledSmbSnapshots-every8hours-keep7days {
    param ([string] $tn)

    $WindowsShareName = Get-Variable -Name "${TN}_WindowsShareName" -ValueOnly

    # Define variables
    $datasetName = $WindowsShareName 
    $datasetPath = "$poolName/$datasetName"

    # Define the desired snapshot task payload
    $snapshotTask = @{
        naming_schema = "%Y_%m_%d_%H_%M_8Hour_Snapshot_Task" # Adjusted naming schema
        recursive = $false
        lifetime_value = 7
        lifetime_unit = "DAY"
        schedule = @{
            minute = "0"
            hour = "*/8"
            dom = "*"
            month = "*"
            dow = "*"
        }
        enabled = $true
        dataset = $datasetPath
    }  

    $snapshotTaskPayload = $snapshotTask | ConvertTo-Json

    # Get existing snapshot tasks
    $existingSnapshotTasks = Invoke-TrueNASAPI -TN $tn -Method Get -Url "pool/snapshottask"
    
    # Check if the desired snapshot task already exists
    $taskExists = $false
    foreach ($task in $existingSnapshotTasks) {
        if ($task.dataset -eq $datasetPath -and $task.naming_schema -eq $snapshotTask.naming_schema) {
            $taskExists = $true
            # Check if the configuration is the same
            if ($task.recursive -eq $snapshotTask.recursive -and 
                $task.lifetime_value -eq $snapshotTask.lifetime_value -and 
                $task.lifetime_unit -eq $snapshotTask.lifetime_unit -and 
                $task.schedule.minute -eq $snapshotTask.schedule.minute -and
                $task.schedule.hour -eq $snapshotTask.schedule.hour -and
                $task.schedule.dom -eq $snapshotTask.schedule.dom -and
                $task.schedule.month -eq $snapshotTask.schedule.month -and
                $task.schedule.dow -eq $snapshotTask.schedule.dow -and
                $task.enabled -eq $snapshotTask.enabled) {
                Write-Host "Snapshot task for dataset [$datasetPath] with the naming schema [$($snapshotTask.naming_schema)] and the desired configuration already exists. No action needed."
                return
            } else {
                Write-Host "Snapshot task for dataset [$datasetPath] with the naming schema [$($snapshotTask.naming_schema)] exists but with a different configuration. Updating..."
                # Update the existing snapshot task
                $updateSnapshotTaskPayload = @{
                    naming_schema = $snapshotTask.naming_schema
                    recursive = $snapshotTask.recursive
                    lifetime_value = $snapshotTask.lifetime_value
                    lifetime_unit = $snapshotTask.lifetime_unit
                    schedule = $snapshotTask.schedule
                    enabled = $snapshotTask.enabled
                    dataset = $snapshotTask.dataset
                } | ConvertTo-Json
                Invoke-TrueNASAPI -TN $tn -Method Put -Url "pool/snapshottask/$($task.id)" -body $updateSnapshotTaskPayload
                Write-Host "Snapshot task for dataset [$datasetPath] with the naming schema [$($snapshotTask.naming_schema)] updated successfully."
                return
            }
        }
    }

    # Create the periodic snapshot task if it does not exist
    if (-not $taskExists) {
        Invoke-TrueNASAPI -TN $tn -Method Post -Url "pool/snapshottask" -body $snapshotTaskPayload
        Write-Host "Snapshot task for dataset [$datasetPath] with the naming schema [$($snapshotTask.naming_schema)] created successfully."
    }
}

Function ShowSnapshotTasks {
    param ([string] $tn)

    $ShowSnapshotTasks = Invoke-TrueNASAPI -TN $tn -Method Get -Url "pool/snapshottask"
    $ShowSnapshotTasks
}

Function SetSmbAcl {
    param ([string] $tn)


    $WindowsShareName = Get-Variable -Name "${TN}_WindowsShareName" -ValueOnly

    # Define dataset parameters
    $datasetName = $WindowsShareName 
    $SmbShareName = $datasetName
    $datasetPath = "$poolName/$datasetName"
    $datasetMntPath = "/mnt/$poolName/$datasetName"

    # Show current ACL
    $AclProperties = @{
        path = $datasetMntPath 
        simplified = $true
        resolve_ids = $false
    }
    $AclPropertiesJson = $AclProperties | ConvertTo-Json -Depth 100

    $AclPropertiesResponse = Invoke-TrueNASAPI -TN $tn -Method Post -Url "filesystem/getacl" -body $AclPropertiesJson
    $currentAcl = $AclPropertiesResponse.acl

    Write-Output "Current ACL:"
    $currentAcl | Format-Table -AutoSize

    # Define the desired ACL payload
    $desiredAcl = @(
        @{
            tag="owner@"
            id=-1
            perms=@{BASIC="FULL_CONTROL"}
            flags=@{BASIC="INHERIT"}
            type="ALLOW"
        },
        @{
            tag="group@"
            id=-1
            perms=@{BASIC="MODIFY"}
            flags=@{BASIC="INHERIT"}
            type="ALLOW"
        },
        @{
            tag="GROUP"
            id=3000
            perms=@{BASIC="FULL_CONTROL"}
            flags=@{BASIC="INHERIT"}
            type="ALLOW"
        }
    )

    # Compare current ACL with the desired ACL
    $aclMatches = $true
    if ($currentAcl.Count -ne $desiredAcl.Count) {
        $aclMatches = $false
    } else {
        for ($i = 0; $i -lt $currentAcl.Count; $i++) {
            if ($currentAcl[$i].tag -ne $desiredAcl[$i].tag -or
                $currentAcl[$i].id -ne $desiredAcl[$i].id -or
                $currentAcl[$i].perms.BASIC -ne $desiredAcl[$i].perms.BASIC -or
                $currentAcl[$i].flags.BASIC -ne $desiredAcl[$i].flags.BASIC -or
                $currentAcl[$i].type -ne $desiredAcl[$i].type) {
                $aclMatches = $false
                break
            }
        }
    }

    if ($aclMatches) {
        Write-Output "ACL for dataset [$datasetPath] is already set to the desired configuration. No action needed."
    } else {
        Write-Output "ACL for dataset [$datasetPath] is different from the desired configuration. Updating..."

        $AclPayload = @{
            dacl = $desiredAcl
            uid = 0
            gid = 0
            path = $datasetMntPath 
            nfs41_flags = @{
                protected = $false
                defaulted = $false
                autoinherit = $false
            }
            acltype = "NFS4"
        }

        $AclPayloadJson = $AclPayload | ConvertTo-Json -Depth 100

        $AclSetResponseID = Invoke-TrueNASAPI -TN $tn -Method Post -Url "filesystem/setacl" -body $AclPayloadJson
        $jobUrl = "core/get_jobs/?id=" + $AclSetResponseID

        do {
            $GetState = Invoke-TrueNASAPI -TN $tn -Method Get -Url $jobUrl
            $GetState
            if ($GetState.State -eq "RUNNING") {
                Write-Output "please wait"
                Start-Sleep -Seconds 10
            }
        } while ($GetState.State -eq "RUNNING")

        Write-Output "ACL for dataset [$datasetPath] updated successfully."
    }
}

Function SetupSShConnection {
    param ([string] $tn)

    $ReplUser = Get-Variable -Name "${TN}_ReplicationAccount" -ValueOnly
    $ReplPassword = Get-Variable -Name "${TN}_ReplicationPassword" -ValueOnly

    # Define ssh setup properties
    $sshSetupProperties = @{
        connection_name = "test2"
        setup_type = "SEMI-AUTOMATIC"
        semi_automatic_setup = @{
            url = "http://10.23.252.38"
            verify_ssl = $false
            admin_username = "admin"
            password = "password"
            username = "root"
            connect_timeout = 10
            sudo = $false
        }
        private_key = @{
            name = "test2_private_key"  # Provide a name for the private key
        }
    }

    $sshSetupPropertiesJson = $sshSetupProperties | ConvertTo-Json

    # Check if the SSH connection already exists
    $existingConnections = Invoke-TrueNASAPI -TN $tn -Method Get -Url "keychaincredential"
    $existingConnection = $existingConnections | Where-Object { $_.name -eq $sshSetupProperties.connection_name }
    $existingPrivateKey = $existingConnections | Where-Object { $_.name -eq $sshSetupProperties.private_key.name }

    if ($existingConnection) {
        Write-Host "SSH connection [$($sshSetupProperties.connection_name)] already exists. Checking configuration..."

        # Check if the existing connection matches the desired configuration
        $matches = $true
        if ($existingConnection.setup_type -ne $sshSetupProperties.setup_type) { $matches = $false }
        if ($existingConnection.semi_automatic_setup.url -ne $sshSetupProperties.semi_automatic_setup.url) { $matches = $false }
        if ($existingConnection.semi_automatic_setup.admin_username -ne $sshSetupProperties.semi_automatic_setup.admin_username) { $matches = $false }
        if ($existingConnection.semi_automatic_setup.username -ne $sshSetupProperties.semi_automatic_setup.username) { $matches = $false }

        if ($matches) {
            Write-Host "SSH connection [$($sshSetupProperties.connection_name)] already exists and matches the desired configuration. No action needed."
            return
        } else {
            Write-Host "SSH connection [$($sshSetupProperties.connection_name)] exists but with different configuration. Attempting to update..."
            # Optionally, you can delete the existing connection if it doesn't match
            # Invoke-TrueNASAPI -TN $tn -Method Delete -Url "keychaincredential/$($existingConnection.id)"
        }
    }

    if ($existingPrivateKey) {
        Write-Host "Private key [$($sshSetupProperties.private_key.name)] already exists. Using existing key..."
        # If the private key already exists, use the existing key id
        $sshSetupProperties.private_key.id = $existingPrivateKey.id
        $sshSetupPropertiesJson = $sshSetupProperties | ConvertTo-Json
    }

    try {
        # Create or update the SSH connection
        $sshSetupResponse = Invoke-TrueNASAPI -TN $tn -Method Post -Url "keychaincredential/setup_ssh_connection" -body $sshSetupPropertiesJson
        Write-Host "SSH connection setup response: $($sshSetupResponse | ConvertTo-Json -Depth 10)"
    } catch {
        Write-Host "Error setting up SSH connection: $($_.Exception.Message)"
        if ($_.Exception.Response -and $($_.Exception.Response.StatusCode) -eq 422) {
            $reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())
            $errorResponse = $reader.ReadToEnd() | ConvertFrom-Json
            $reader.Close()

            if ($errorResponse.'setup_ssh_connection.private_key.name') {
                Write-Host "Error: Private key name is already in use by another SSH Key pair."
                # Additional logic to handle the error (e.g., choosing a different name or using the existing key)
            }
            if ($errorResponse.'setup_ssh_connection.connection_name') {
                Write-Host "Error: Connection name is already in use by another Keychain Credential."
                # Additional logic to handle the error (e.g., choosing a different name or using the existing connection)
            }
        }
    }
}

Function ListAllKeychainCredentials {
    param ([string] $tn)

    $credentials = Invoke-TrueNASAPI -TN $tn -Method Get -Url "keychaincredential"
    return $credentials
}

Function CreateReplicationTasksForSnapshots {
    param (
        [string] $sourceTN,
        [string] $targetTN
    )

    # Get the list of snapshot tasks from the source TrueNAS system
    $snapshotTasks = Invoke-TrueNASAPI -TN $sourceTN -Method Get -Url "pool/snapshottask"

    # Get the SSH credential ID
    $sshCredentials = Invoke-TrueNASAPI -TN $sourceTN -Method Get -Url "keychaincredential"
    $sshCredential = $sshCredentials | Where-Object { $_.type -eq "SSH_CREDENTIALS" }
    if (-not $sshCredential) {
        Write-Host "No SSH credential found. Please ensure an SSH credential exists."
        return
    }
    $sshCredentialId = $sshCredential.id

    # Get existing replication tasks
    $existingReplicationTasks = Invoke-TrueNASAPI -TN $sourceTN -Method Get -Url "replication"

    foreach ($snapshotTask in $snapshotTasks) {
        $snapshotTaskId = $snapshotTask.id
        $snapshotTaskDataset = $snapshotTask.dataset

        # Define the target dataset for replication
        $targetDataset = $snapshotTaskDataset -replace "^([^/]+)/", "`$1/$ReplicationPrependText"

        # Define the desired replication task payload
        $desiredReplicationTask = @{
            name = "Replication_${snapshotTaskId}"
            auto = $true
            retention_policy = "SOURCE"
            allow_from_scratch = $true
            periodic_snapshot_tasks = @($snapshotTaskId)
            target_dataset = $targetDataset
            source_datasets = @($snapshotTaskDataset)
            recursive = $false
            direction = "PUSH"
            transport = "SSH"
            enabled = $true
            readonly = "SET"
            compressed = $true
            ssh_credentials = $sshCredentialId
        }

        # Convert to JSON for comparison
        $desiredReplicationTaskJson = $desiredReplicationTask | ConvertTo-Json -Depth 10

        # Check if the replication task already exists
        $existingTask = $existingReplicationTasks | Where-Object { $_.name -eq "Replication_${snapshotTaskId}" }

        if ($existingTask) {
            # Extract the relevant properties for comparison
            $existingTaskConfig = @{
                auto = $existingTask.auto
                ssh_credentials = $existingTask.ssh_credentials.id
                periodic_snapshot_tasks = $existingTask.periodic_snapshot_tasks.id
                source_datasets = $existingTask.source_datasets
                recursive = $existingTask.recursive
                readonly = $existingTask.readonly
                name = $existingTask.name
                direction = $existingTask.direction
                retention_policy = $existingTask.retention_policy
                allow_from_scratch = $existingTask.allow_from_scratch
                transport = $existingTask.transport
                target_dataset = $existingTask.target_dataset
                enabled = $existingTask.enabled
                compressed = $existingTask.compressed
            }

            # Compare existing task configuration with the desired configuration
            $updateNeeded = $false

            Write-Host "Comparing existing task with desired task:"
            foreach ($key in $desiredReplicationTask.Keys) {
                if ($desiredReplicationTask[$key] -ne $existingTaskConfig[$key]) {
                    Write-Host "Difference found in key: $key"
                    Write-Host "Existing value: $($existingTaskConfig[$key])"
                    Write-Host "Desired value: $($desiredReplicationTask[$key])"
                    $updateNeeded = $true
                }
            }

            if ($updateNeeded) {
                # Update the existing replication task only if configurations do not match
                $replicationTaskId = $existingTask.id
                $updateResponse = Invoke-TrueNASAPI -TN $sourceTN -Method Put -Url "replication/id/$replicationTaskId" -Body $desiredReplicationTaskJson

                if ($updateResponse) {
                    Write-Host "Replication task for snapshot task [$snapshotTaskId] updated successfully."
                } else {
                    Write-Host "Failed to update replication task for snapshot task [$snapshotTaskId]."
                }
            } else {
                Write-Host "Replication task for snapshot task [$snapshotTaskId] is already up-to-date. No changes made."
            }
        } else {
            # Create a new replication task
            $createResponse = Invoke-TrueNASAPI -TN $sourceTN -Method Post -Url "replication" -Body $desiredReplicationTaskJson

            if ($createResponse) {
                Write-Host "Replication task for snapshot task [$snapshotTaskId] created successfully."
            } else {
                Write-Host "Failed to create replication task for snapshot task [$snapshotTaskId]."
            }
        }
    }
}

Function ShowReplicationTask{
    param ([string] $tn)



    $GetReplication = Invoke-TrueNASAPI -TN $tn -Method Get -Url "replication"
    $GetReplication

}

Function SetSnmpService {
    param ([string] $tn)

    # Desired SNMP configuration
    $desiredCommunity = $Script:SnmpRo
    $desiredEnable = $Script:SnmpEnable

    # Get current SNMP configuration
    $snmpConfig = Invoke-TrueNASAPI -TN $tn -Method Get -Url "snmp"
    $currentCommunity = $snmpConfig.community

    # Check if SNMP community needs to be updated
    if ($currentCommunity -ne $desiredCommunity) {
        # Update SNMP community
        $snmpPayload = @{
            community = $desiredCommunity
        }

        $snmpPayloadJson = $snmpPayload | ConvertTo-Json -Depth 10
        $updateSnmpResponse = Invoke-TrueNASAPI -TN $tn -Method Put -Url "snmp" -Body $snmpPayloadJson

        if ($updateSnmpResponse -ne $null) {
            Write-Host "SNMP community updated successfully."
        } else {
            Write-Host "Failed to update SNMP community."
        }
    } else {
        Write-Host "SNMP community is already up-to-date. No changes made."
    }

    # Ensure SNMP service is enabled and running
    $services = Invoke-TrueNASAPI -TN $tn -Method Get -Url "service"
    $snmpService = $services | Where-Object { $_.service -eq "snmp" }

    if ($snmpService.enable -ne $desiredEnable) {
        # Enable or disable the SNMP service
        $enableSnmpPayload = @{
            enable = $desiredEnable
        }
        $enableSnmpPayloadJson = $enableSnmpPayload | ConvertTo-Json -Depth 10
        $enableSnmpResponse = Invoke-TrueNASAPI -TN $tn -Method Put -Url "service/id/$($snmpService.id)" -Body $enableSnmpPayloadJson

        if ($enableSnmpResponse -ne $null) {
            Write-Host "SNMP service state updated successfully."
        } else {
            Write-Host "Failed to update SNMP service state."
        }
    } else {
        Write-Host "SNMP service state is already set as desired."
    }

    if ($snmpService.state -eq "RUNNING") {
        Write-Host "SNMP service is already running. No action needed."
    } else {
        # Start the SNMP service
        $startServicePayload = @{
            service = "snmp"
        }
        $startServicePayloadJson = $startServicePayload | ConvertTo-Json -Depth 10

        try {
            $startServiceResponse = Invoke-TrueNASAPI -TN $tn -Method Post -Url "service/start" -Body $startServicePayloadJson

            # Check the response
            if ($startServiceResponse -eq $true) {
                Write-Host "SNMP service started successfully."
            } else {
                Write-Host "Failed to start SNMP service."
            }
        } catch {
            Write-Host "Failed to start SNMP service. Error: $_"
        }
    }
}

Function EnabledHttpRedirect {
    param ([string] $tn)

    # Retrieve current HTTPS settings
    $httpResponse = Invoke-TrueNASAPI -TN $tn -Method Get -Url "system/general"
    $ui_port = $httpResponse.ui_port
    $ui_httpsport = $httpResponse.ui_httpsport
    $ui_httpsredirect = $httpResponse.ui_httpsredirect
    $ui_httpsprotocols = $httpResponse.ui_httpsprotocols

    Write-host "Current HTTP/HTTPS settings"
    $ui_port
    $ui_httpsport
    $ui_httpsredirect
    $ui_httpsprotocols

    # Check if HTTPS is enabled
    if (-not $ui_httpsredirect) {
        # Configure HTTPS settings (you'll need to replace placeholders with actual certificate paths etc.)
        $EnableRedirects = @{ "ui_httpsredirect" = $true }
        $EnableRedirectsJson = $EnableRedirects | ConvertTo-Json

        $configureHttpsResponse = Invoke-TrueNASAPI -TN $tn -Method Put -Url "system/general" -body $EnableRedirectsJson

        # Check if configuration was successful
        if ($configureHttpsResponse.ui_httpsredirect  -eq $true) {
            Write-Host "HTTPS configured successfully."
        } else {
            Write-Host "Failed to configure HTTPS."
        }
    }

}

Function DisableHttpRedirect {
    param ([string] $tn)

    # Retrieve current HTTPS settings
    $httpResponse = Invoke-TrueNASAPI -TN $tn -Method Get -Url "system/general"
    $ui_httpsredirect = $httpResponse.ui_httpsredirect

    if ($ui_httpsredirect) {
        Write-Host "HTTP to HTTPS redirect is currently enabled. Disabling it now..."

        # Disable HTTPS redirect
        $disableRedirects = @{ "ui_httpsredirect" = $false }
        $disableRedirectsJson = $disableRedirects | ConvertTo-Json

        $disableRedirectResponse = Invoke-TrueNASAPI -TN $tn -Method Put -Url "system/general" -Body $disableRedirectsJson

        # Check if the response indicates success
        if ($disableRedirectResponse.ui_httpsredirect -eq $false) {
            Write-Host "HTTP to HTTPS redirect has been disabled successfully."
        } else {
            Write-Host "Failed to disable HTTP to HTTPS redirect. Response:"
            Write-Host ($disableRedirectResponse | ConvertTo-Json -Depth 100)
        }
    } else {
        Write-Host "HTTP to HTTPS redirect is already disabled. No action needed."
    }
}

Function TestHttpRedirect {
    param ([string] $tn)

    # Retrieve current HTTPS settings
    $httpResponse = Invoke-TrueNASAPI -TN $tn -Method Get -Url "system/general"
    
    # Check if HTTPS redirect is enabled
    $ui_httpsredirect = $httpResponse.ui_httpsredirect

    if ($ui_httpsredirect) {
        Write-Host "HTTP to HTTPS redirect is enabled."
    } else {
        Write-Host "HTTP to HTTPS redirect is not enabled."
    }
}

Function SetSmbServiceAutostart {
    param ([string] $tn)

    # Retrieve the current status of the SMB service
    $services = Invoke-TrueNASAPI -TN $tn -Method Get -Url "service"
    $smbService = $services | Where-Object { $_.service -eq 'cifs' }
    $smbId = $smbService.id
    $currentAutostart = $smbService.enable

    # Check if the SMB service is already set to autostart
    if ($currentAutostart -eq $true) {
        Write-Host "SMB service is already set to autostart. No action needed."
    } else {
        # Set SMB service to start on boot
        $smbServiceAutostart = @{
            enable = $true
        }
        $smbServiceAutostartJson = $smbServiceAutostart | ConvertTo-Json 

        $updateResponse = Invoke-TrueNASAPI -TN $tn -Method Put -Url "service/id/$smbId" -Body $smbServiceAutostartJson

        # Check the response
        if ($updateResponse -ne $null) {
            Write-Host "SMB service set to autostart successfully. Response:"
            Write-Host ($updateResponse | ConvertTo-Json -Depth 100)
        } else {
            Write-Host "Failed to set SMB service to autostart. Response:"
            Write-Host ($updateResponse | ConvertTo-Json -Depth 100)
        }
    }

    # Start the SMB service if it's not already running
    if ($smbService.state -ne "RUNNING") {
        $startServicePayload = @{
            service = "cifs"
        }
        $startServicePayloadJson = $startServicePayload | ConvertTo-Json -Depth 10

        try {
            $startServiceResponse = Invoke-TrueNASAPI -TN $tn -Method Post -Url "service/start" -Body $startServicePayloadJson

            # Check the response
            if ($startServiceResponse -eq $true) {
                Write-Host "SMB service started successfully."
            } else {
                Write-Host "Failed to start SMB service."
            }
        } catch {
            Write-Host "Failed to start SMB service. Error: $_"
        }
    } else {
        Write-Host "SMB service is already running. No action needed."
    }
}

FYI the TrueNAS python API client works in python on windows. GitHub - truenas/api_client