FortigateManager.psm1

$script:ModuleRoot = $PSScriptRoot
$script:ModuleVersion = (Import-PowerShellDataFile -Path "$($script:ModuleRoot)\FortigateManager.psd1").ModuleVersion

# Detect whether at some level dotsourcing was enforced
$script:doDotSource = Get-PSFConfigValue -FullName FortigateManager.Import.DoDotSource -Fallback $false
if ($FortigateManager_dotsourcemodule) { $script:doDotSource = $true }

<#
Note on Resolve-Path:
All paths are sent through Resolve-Path/Resolve-PSFPath in order to convert them to the correct path separator.
This allows ignoring path separators throughout the import sequence, which could otherwise cause trouble depending on OS.
Resolve-Path can only be used for paths that already exist, Resolve-PSFPath can accept that the last leaf my not exist.
This is important when testing for paths.
#>


# Detect whether at some level loading individual module files, rather than the compiled module was enforced
$importIndividualFiles = Get-PSFConfigValue -FullName FortigateManager.Import.IndividualFiles -Fallback $false
if ($FortigateManager_importIndividualFiles) { $importIndividualFiles = $true }
if (Test-Path (Resolve-PSFPath -Path "$($script:ModuleRoot)\..\.git" -SingleItem -NewChild)) { $importIndividualFiles = $true }
if ("<was compiled>" -eq '<was not compiled>') { $importIndividualFiles = $true }
    
function Import-ModuleFile
{
    <#
        .SYNOPSIS
            Loads files into the module on module import.
         
        .DESCRIPTION
            This helper function is used during module initialization.
            It should always be dotsourced itself, in order to proper function.
             
            This provides a central location to react to files being imported, if later desired
         
        .PARAMETER Path
            The path to the file to load
         
        .EXAMPLE
            PS C:\> . Import-ModuleFile -File $function.FullName
     
            Imports the file stored in $function according to import policy
    #>

    [CmdletBinding()]
    Param (
        [string]
        $Path
    )
    
    $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($Path).ProviderPath
    if ($doDotSource) { . $resolvedPath }
    else { $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText($resolvedPath))), $null, $null) }
}

#region Load individual files
if ($importIndividualFiles)
{
    # Execute Preimport actions
    foreach ($path in (& "$ModuleRoot\internal\scripts\preimport.ps1")) {
        . Import-ModuleFile -Path $path
    }
    
    # Import all internal functions
    foreach ($function in (Get-ChildItem "$ModuleRoot\internal\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore))
    {
        . Import-ModuleFile -Path $function.FullName
    }
    
    # Import all public functions
    foreach ($function in (Get-ChildItem "$ModuleRoot\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore))
    {
        . Import-ModuleFile -Path $function.FullName
    }
    
    # Execute Postimport actions
    foreach ($path in (& "$ModuleRoot\internal\scripts\postimport.ps1")) {
        . Import-ModuleFile -Path $path
    }
    
    # End it here, do not load compiled code below
    return
}
#endregion Load individual files

#region Load compiled code
<#
This file loads the strings documents from the respective language folders.
This allows localizing messages and errors.
Load psd1 language files for each language you wish to support.
Partial translations are acceptable - when missing a current language message,
it will fallback to English or another available language.
#>

Import-PSFLocalizedString -Path "$($script:ModuleRoot)\en-us\*.psd1" -Module 'FortigateManager' -Language 'en-US'

function Convert-FMTimestampToDate {
    <#
    .SYNOPSIS
    Helper function to convert Unix timestamps to DateTime.
 
    .DESCRIPTION
    Helper function to convert Unix timestamps to DateTime.
 
    .PARAMETER TimeStamp
    The timestamp to be converted
 
    .EXAMPLE
    Convert-FMTimestampToDate -TimeStamp 1655983275
 
    Returns 'Thursday, 23 June 2022 11:21:15'
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true,
                   Position=0,
                   ValueFromPipeline=$true)]
        $TimeStamp
    )
    process{
        return (Get-Date 01.01.1970) + ([System.TimeSpan]::fromseconds($TimeStamp))
    }
}

function ConvertTo-FMFilterArray {
    <#
    .SYNOPSIS
    Converts filter strings into the array type the API aspects.
 
    .DESCRIPTION
    Converts filter strings into the array type the API aspects.
    See about_FortigateManagerFilter
 
    .PARAMETER Filter
    The filter String in the following format:
    "{attribute} {operator} {value}"
 
    - The attribute depends on the object model you are querying.
    - the operator is one of the following:
      -eq
      -like (use % (multi) and _ (single char) as a wildcard)
      -contain (NO LIKE COMPARISON, checks if something is contained within an array)
      -ne
      -notlike
    - The value is the value used for filtering
 
    Example:
 
 
    .EXAMPLE
    Get-FMAddress -Filter "name -eq srv123"
 
    Returns the array @('name','==','srv123')
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [parameter(mandatory = $false, ValueFromPipeline = $true, ParameterSetName = "default")]
        [string[]]$Filter
    )

    begin {
        $resultArray = @()
        $filterInputArray = @()
        $operatorTranslation = @{
            "-eq"       = "=="
            "-ne"       = "!=="
            "-like"     = "like"
            "-notlike"  = "!like"
            "-contains" = "contain"
        }
    }

    process {
        if ($Filter) { $filterInputArray += $Filter }
    }

    end {
        foreach ($filterString in $filterInputArray) {
            Write-PSFMessage "Analysiere '$filterString'"
            $regexResults = [regex]::Matches($filterString, "(?<attribute>.*) (?<operator>-eq|-ne|-notlike|-like|-contains) (?<value>.*)")
            Write-PSFMessage "`$regexResults=$($regexResults)"
            if ($regexResults) {
                $attribute = $regexResults[0].Groups["attribute"].value
                $operator = $operatorTranslation."$($regexResults[0].Groups["operator"].value)"
                $value = $regexResults[0].Groups["value"].value
                if ($operator -like '!*') {
                    $currentFilter = @("!")
                    $operator = $operator.Trim("!")
                }
                else {
                    $currentFilter = @()
                }
                $currentFilter += @($attribute, $operator, $value)
                $resultArray += , ($currentFilter)
            }
            else {
                Write-PSFMessage -Level Warning "No valid filter string: $filterString"
            }
        }
        Write-PSFMessage "Result= $($resultArray|ConvertTo-Json)"
        return $resultArray
    }
}

function ConvertTo-FMStartEndIp {
    <#
    .SYNOPSIS
    Helper for extracting start- and end-ip from a iprange in a string.
 
    .DESCRIPTION
    Helper for extracting start- and end-ip from a iprange in a string.
    Returns an array, $result[0] is the start and $result[1] the end ip
 
    .PARAMETER IPRange
    The ipRange
 
    .EXAMPLE
    ConvertTo-FMStartEndIp -IPRange "1.1.1.1-1.1.2.3"
 
    Returns @("1.1.1.1","1.1.2.3")
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([object[]])]
    param (
        [string]$IpRange
    )
    $returnArray=@(0..1)
    Write-PSFMessage "Extracting Start- and End-IP from Range $IpRange"
    $regex = "(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)[ -]+(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b)"
    $rangeMatches = $IpRange | Select-String -Pattern $regex
    $returnArray[0] = $rangeMatches.Matches[0].Groups[1].Value
    $returnArray[1] = $rangeMatches.Matches[0].Groups[2].Value
    Write-PSFMessage "StartIP=$($returnArray[0]) EndIp=$($returnArray[1])"
    return $returnArray
}

function ConvertTo-FMUrlPart {
    <#
    .SYNOPSIS
    Helper funtion to escape characters which cannot be used within an URL parameter.
 
    .DESCRIPTION
    Helper funtion to escape characters which cannot be used within an URL parameter.
 
    .PARAMETER Input
    The parameter which has to be replaced.
 
    .EXAMPLE
    $apiCallParameter.Path = "/pm/config/adom/$explicitADOM/obj/firewall/address/$($Name|ConvertTo-FMUrlPart)"
 
    Creates a path for a named address.
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        [string[]]$Input
    )

    begin {
        $resultList=@()
    }

    process {
        foreach($string in $Input){
            $modifiedString=$string -replace '/', "\/"
            Write-PSFMessage -Level Debug "Replacing String $string Result $modifiedString"
            $resultList += $modifiedString
        }
    }

    end {
        if ($resultList.count -eq 1){
            return $resultList[0]
        }
        return $resultList
    }
}

function Remove-FMNullValuesFromHashtable {
    <#
    .SYNOPSIS
    Helper function to remove empty attributes from a provided HashTable.
 
    .DESCRIPTION
    Helper function to remove empty attributes from a provided HashTable.
 
    .PARAMETER InputObject
    The original Hastable
 
    .PARAMETER NullHandler
    How should empty values be handled?
    -Keep: Keep the Attribute
    -RemoveAttribute: Remove the Attribute (default)
    -ClearContent: Clear the value
 
    .PARAMETER LongNullValue
    Which long value should be interpretated as "empty"
 
    .EXAMPLE
    An example
 
    has to be provided later
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([Hashtable])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessforStateChangingFunctions', '')]
    param (
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        # [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "clearEmpty")]
        # [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "removeEmpty")]
        [HashTable]$InputObject,
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute",
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$LongNullValue = -1
        # ,
        # [parameter(mandatory = $false, ParameterSetName = "clearEmpty")]
        # [switch]$ClearEmptyArrays,
        # [parameter(mandatory = $false, ParameterSetName = "keepEmpty")]
        # [switch]$KeepEmptyAttribute
    )

    begin {
        if ($NullHandler -eq "RemoveAttribute") {
            $logLevel = "Debug"
        }
        else {
            $logLevel = "Verbose"
        }
    }

    process {
    }

    end {
        if ($NullHandler -eq "Keep") {
            Write-PSFMessage "Returning inputObject unchanged" -Level $logLevel
            return $InputObject
        }
        $keyList = $InputObject.Keys | ForEach-Object { "$_" }
        foreach ($key in $keyList) {
            if ($null -eq $InputObject.$key) {
                continue
            }
            $paramaterType = $InputObject.$key.gettype()
            switch -regex ($paramaterType) {
                ".*\[\]" {
                    write-psfmessage "Prüfe Null-Arrays" -Level $logLevel
                    if ($InputObject.$key.Count -gt 0 -and ([string]::IsNullOrEmpty($InputObject.$key[0]))) {
                        if ($NullHandler -eq "ClearContent") {
                            write-psfmessage "Replacing Array Attribute $key with empty Array" -Level $logLevel
                            $InputObject.$key = @()
                        }
                        else {
                            write-psfmessage "Removing Array Attribute $key" -Level $logLevel
                            $InputObject.Remove($key)
                        }
                    }
                }
                "string" {
                    if (($NullHandler -eq "RemoveAttribute") -and ([string]::IsNullOrEmpty($InputObject.$key))) {
                        write-psfmessage "Removing String Attribute $key" -Level $logLevel
                        $InputObject.Remove($key)
                    }
                }
                "long" {
                    if ($NullHandler -eq "RemoveAttribute" -and $InputObject.$key -eq $LongNullValue) {
                        write-psfmessage "Removing Long Attribute $key" -Level $logLevel
                        $InputObject.Remove($key)
                    }
                }
                Default { Write-PSFMessage "Unknown ParamaterType $paramaterType" -Level $logLevel }
            }
        }
        return $InputObject
    }
}

function Remove-FMWhitespacesFromAttribute {
    <#
    .SYNOPSIS
    Helper function to trim all string attributes.
 
    .DESCRIPTION
    Helper function to trim all string attributes.
 
    .PARAMETER InputObject
    The original Object/Hashtable.
 
    .PARAMETER CharToTrim
    Which characters should be trimmed? Defaults to whitespace
 
 
    .EXAMPLE
    An example
 
    has to be provided later
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([Object])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessforStateChangingFunctions', '')]
    param (
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        $InputObject,
        [string]$CharToTrim=" "
    )

    begin {    }

    process {
        if ($null -eq $InputObject){
            Write-PSFMessage "Input-Object is Null"
            return
        }
        if ($InputObject -is [HashTable]) {
            $propertyNames = @() + $InputObject.Keys
        }
        else {
            $propertyNames = $InputObject.PSObject.Properties.Name
        }
        Write-PSFMessage "`$propertyNames=$propertyNames"
        foreach ($prop in $propertyNames) {
            switch ($InputObject.$prop.gettype()) {
                "string" {
                    $InputObject.$prop = $InputObject.$prop.Trim($CharToTrim)
                }
            }
        }
        return $InputObject
    }

    end {    }
}

function Resolve-FMAdom {
    <#
    .SYNOPSIS
    Internal Helper for getting the ADOM from the given parameter or from the connection default.
 
    .DESCRIPTION
    Internal Helper for getting the ADOM from the given parameter or from the connection default.
 
    .PARAMETER Connection
    The connection object
 
    .PARAMETER ADOM
    The explicit ADOM.
 
    .PARAMETER EnableException
    Should an exception be thrown if no ADOM can be resolved.
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param (
        [parameter(Mandatory)]
        $Connection,
        [string]$ADOM,
        [bool]$EnableException = $true
    )
    $adomAvailable = "$([string]::IsNullOrEmpty($ADOM) -eq $false)/$([string]::IsNullOrEmpty($connection.forti.defaultADOM) -eq $false)".ToLower()
    Write-PSFMessage "Checking ADOM availibility: $adomAvailable (explicit/default)" -Level Debug
    Write-PSFMessage "`$ADOM=$ADOM" -Level Debug
    Write-PSFMessage "`$connection.forti.defaultADOM=$($connection.forti.defaultADOM)" -Level Debug
    switch -wildcard ($adomAvailable) {
        "true/*" {
            Write-PSFMessage "Explicit ADOM found" -Level Debug
            return $ADOM
        }
        "false/true" {
            Write-PSFMessage "No explicit ADOM but Default found" -Level Debug
            return $connection.forti.defaultADOM
        }
        "false/false" {
            Write-PSFMessage "Neither explicit nor Default ADOM found" -Level Debug
            if ($EnableException) {
                throw "Neither explicit nor Default ADOM found"
            }
            return $null
        }
    }
}

function Resolve-FMRevisionNote {
    <#
    .SYNOPSIS
    Internal Helper for getting the RevisionNote from the given parameter or from the connection default.
 
    .DESCRIPTION
    Internal Helper for getting the RevisionNote from the given parameter or from the connection default.
 
    .PARAMETER Connection
    The connection object
 
    .PARAMETER RevisionNote
    The explicit RevisionNote.
 
    .PARAMETER EnableException
    Should an exception be thrown if no RevisionNote can be resolved.
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param (
        [parameter(Mandatory)]
        $Connection,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    $RevisionNoteAvailable = "$([string]::IsNullOrEmpty($RevisionNote) -eq $false)/$([string]::IsNullOrEmpty($connection.forti.defaultRevisionNote) -eq $false)".ToLower()
    Write-PSFMessage "Checking RevisionNote availibility: $RevisionNoteAvailable (explicit/default)" -Level Debug
    Write-PSFMessage "`$RevisionNote=$RevisionNote" -Level Debug
    Write-PSFMessage "`$connection.forti.defaultRevisionNote=$($connection.forti.defaultRevisionNote)" -Level Debug
    switch -wildcard ($RevisionNoteAvailable) {
        "true/*" {
            Write-PSFMessage "Explicit RevisionNote found" -Level Debug
            return $RevisionNote
        }
        "false/true" {
            Write-PSFMessage "No explicit RevisionNote but Default found" -Level Debug
            return $connection.forti.defaultRevisionNote
        }
        "false/false" {
            Write-PSFMessage "Neither explicit nor Default RevisionNote found" -Level Debug
            if ($EnableException) {
                throw "Neither explicit nor Default RevisionNote found; RevisionNote is needed, provide it for the API-Call or while locking the ADOM, see about_RevisionNote"
            }
            return $null
        }
    }
}

function Test-FMSubnetCidr {
    <#
    .SYNOPSIS
    Helper for verifying that a subnet contains a netmask.
 
    .DESCRIPTION
    Helper for verifying that a subnet contains a netmask.
 
    .PARAMETER Subnet
    The Subnet
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [string]$Subnet
    )
    $Subnet = $Subnet.Trim()
    if ($Subnet -match '^\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b$') {
        Write-PSFMessage "Subnet $Subnet is missing the subnet mask"
        $cidr = ""
        switch -regex ($Subnet) {
            '^\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b$' { $cidr = "/32" }
            '^\b\d{1,3}\.\d{1,3}\.\d{1,3}\.0$' { $cidr = "/24" }
            default {
                Write-PSFMessage "Cannot guess cidr for Subnet $Subnet"
            }
        }
        $Subnet += $cidr
        Write-PSFMessage "New Subnet: $Subnet"
    }
    return $Subnet
}

function Convert-FMApi2Hashtable {
    <#
    .SYNOPSIS
    Helper function for creating Powershell code out of HashTable Definitions in JSON.
 
    .DESCRIPTION
    Helper function for creating Powershell code out of HashTable Definitions in JSON.
 
    .PARAMETER objectName
    The new Object Name for the function Name
 
    .EXAMPLE
    InModuleScope fortigatemanager {Convert-FMApi2HashTable}
 
    Takes the JSON Code from the clipboard and replaces it with an auto generated Powershell function.
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [Parameter()]
        [String]
        $objectName = ""
    )
    $json = Get-Clipboard

    $newLineDelim = @"
,
 
"@

    try {
        $copy2ClipBoardData = @()
        $copy2ClipBoardData += @"
function New-FMObj$objectName {
    [CmdletBinding()]
    param (
"@

        $defParameter = @()
        $defHashMap = @()
        $sourceHashTable = $json | ConvertFrom-Json -ErrorAction Stop | convertto-psfhashtable
        $sourceKeyList = $sourceHashTable.Keys | Sort-Object
        foreach ($sourceKey in $sourceKeyList) {
            $parameterName = [regex]::Replace($sourceKey.Trim('_'), '(?i)(?:^|-| )(\p{L})', { $args[0].Groups[1].Value.ToUpper() })
            $parameterType = $sourceHashTable.$sourceKey.gettype()
            $parameterValue = $sourceHashTable.$sourceKey
            Write-PSFMessage "`$parameterName=$parameterName; Type=$parameterType;value=$parameterValue"
            # $defParameter+=@"
            # [parameter(mandatory = `$false, ParameterSetName = "default")]
            # [$parameterType]`$$parameterName
            # "@
            # Hashtable Definition ergänzen
            switch ($parameterType) {
                "long" {
                    $defParameter += @"
        [parameter(mandatory = `$false, ParameterSetName = "default")]
        [$parameterType]`$$parameterName=-1
"@

                }
                Default {
                    switch -regex ($parameterValue) {
                        "disable|disable" {
                            $defParameter += @"
            [parameter(mandatory = `$false, ParameterSetName = "default")]
            [ValidateSet("disable", "enable")]
            [$parameterType]`$$parameterName
"@

                        }
                        Default {
                            $defParameter += @"
            [parameter(mandatory = `$false, ParameterSetName = "default")]
            [$parameterType]`$$parameterName
"@

                        }
                    }
                }
            }
            # Hashtable Definition ergänzen
            switch ($parameterType) {
                "System.Object[]" { $defHashMap += "'$sourceKey'=@(`$$parameterName)" }
                "string" { $defHashMap += "'$sourceKey'=`"`$$parameterName`"" }
                "long" { $defHashMap += "'$sourceKey'=`$$parameterName" }
                Default { Write-PSFMessage -Level Warning "Unknown ParamaterType $parameterType" }
            }
        }
        $defParameter += @"
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = `$false, ValueFromPipeline = `$false, ParameterSetName = "default")]
        `$NullHandler = "RemoveAttribute"
"@

        $copy2ClipBoardData += ($defParameter | Join-String $newLineDelim)
        $copy2ClipBoardData += ")"
        $copy2ClipBoardData += "`$data=@{"
        $copy2ClipBoardData += ($defHashMap | Out-String)
        $copy2ClipBoardData += "}"
        $copy2ClipBoardData += "return `$data | Remove-FMNullValuesFromHashtable -NullHandler `$NullHandler"
        $copy2ClipBoardData += "}"
        Write-PSFMessage -Level Host ($copy2ClipBoardData  | out-string)
        $copy2ClipBoardData  | out-string | Set-Clipboard
    }
    catch {
        Write-PSFMessage -Level Warning "Clipboard did not contain a JSON String, $_"
    }
}

# function Convert-FMApiGet2FunctionStub {
# <#
# .SYNOPSIS
# Helper function for creating Powershell code out of HashTable Definitions in JSON.

# .DESCRIPTION
# Helper function for creating Powershell code out of HashTable Definitions in JSON.

# .PARAMETER objectName
# The new Object Name for the function Name

# .EXAMPLE
# InModuleScope fortigatemanager {Convert-FMApi2HashTable}

# Takes the JSON Code from the clipboard and replaces it with an auto generated Powershell function.

# .NOTES
# General notes
# #>
# [CmdletBinding()]
# param (
# [parameter(Mandatory = $false)]
# $Connection = (Get-FMLastConnection),
# [Parameter()]
# [String]
# $objectName = "",
# [string]$Url
# )
# $copy2ClipBoardData = @()
# $apiCallParameter = @{
# EnableException = $true
# Connection = $Connection
# LoggingAction = "Query-Syntax"
# LoggingActionValues = $Url
# method = "get"
# Parameter = @{
# 'option' = "syntax"
# }
# Path = $Url
# }
# $newLineCommaDelim = @"
# ,

# "@
# $newLineDelim = @"


# "@

# $result = Invoke-FMAPI @apiCallParameter
# $global:syntax = $result.result[0]
# try {
# $objectName = $syntax.data.PSObject.Properties.name
# $objectNameCC = ConvertTo-CamelCase $objectName
# $copy2ClipBoardData = @()
# $copy2ClipBoardData += @"
# function New-FMObj$objectNameCC {
# <#
# .SYNOPSIS
# Creates a new $objectName object with the given attributes.

# .DESCRIPTION
# Creates a new $objectName object with the given attributes.
# "@
# $defHelp = @()
# $defParameter = @()
# $defHashMap = @()
# #region Insert Parsed Data
# $attributes = $syntax.data.$objectName.attr
# $sortedAttributeNameList = $attributes.PSObject.Properties.name|Sort-Object
# foreach ($attr in $sortedAttributeNameList) {
# $parameterName = ConvertTo-CamelCase $attr
# # $parameterName = [regex]::Replace($sourceKey.Trim('_'), '(?i)(?:^|-| )(\p{L})', { $args[0].Groups[1].Value.ToUpper() })
# $defHelp+=(" .PARAMETER $parameterName")
# if ($attributes.$attr.help){
# $defHelp+=(" $($attributes.$attr.help)")
# }
# $defHelp+=(" This parameter is stored in the API attribute $attr.")
# if ($attributes.$attr.default){
# $defHelp+=(" Default Value: $($attributes.$attr.default)")
# }
# $defHelp+=$newLineDelim
# # $parameterType = $sourceHashTable.$sourceKey.gettype()
# # $parameterValue = $sourceHashTable.$sourceKey
# Write-PSFMessage "`$parameterName=$parameterName; Type=$parameterType;value=$parameterValue"
# # $defParameter+=@"
# # [parameter(mandatory = `$false, ParameterSetName = "default")]
# # [$parameterType]`$$parameterName
# # "@
# # Hashtable Definition ergänzen
# # switch ($parameterType) {
# # "long" {
# # $defParameter += @"
# # [parameter(mandatory = `$false, ParameterSetName = "default")]
# # [$parameterType]`$$parameterName=-1
# # "@
# # }
# # Default {
# # switch -regex ($parameterValue) {
# # "disable|disable" {
# # $defParameter += @"
# # [parameter(mandatory = `$false, ParameterSetName = "default")]
# # [ValidateSet("disable", "enable")]
# # [$parameterType]`$$parameterName
# # "@
# # }
# # Default {
# # $defParameter += @"
# # [parameter(mandatory = `$false, ParameterSetName = "default")]
# # [$parameterType]`$$parameterName
# # "@
# # }
# # }
# # }
# # }
# # Hashtable Definition ergänzen
# # switch ($parameterType) {
# # "System.Object[]" { $defHashMap += "'$sourceKey'=@(`$$parameterName)" }
# # "string" { $defHashMap += "'$sourceKey'=`"`$$parameterName`"" }
# # "long" { $defHashMap += "'$sourceKey'=`$$parameterName" }
# # Default { Write-PSFMessage -Level Warning "Unknown ParamaterType $parameterType" }
# # }
# }
# #endregion
# $defHelp+=@"
# .EXAMPLE
# Example has to be

# implemented

# .NOTES
# General notes
# #>
# "@
# $defParameter += @"
# [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
# [parameter(mandatory = `$false, ValueFromPipeline = `$false, ParameterSetName = "default")]
# `$NullHandler = "RemoveAttribute"
# "@
# $copy2ClipBoardData += ($defHelp | Join-String $newLineDelim)
# $copy2ClipBoardData += ($defParameter | Join-String $newLineCommaDelim)
# $copy2ClipBoardData += ")"
# $copy2ClipBoardData += "`$data=@{"
# $copy2ClipBoardData += ($defHashMap | Out-String)
# $copy2ClipBoardData += "}"
# $copy2ClipBoardData += "return `$data | Remove-FMNullValuesFromHashtable -NullHandler `$NullHandler"
# $copy2ClipBoardData += "}"

# return $copy2ClipBoardData | out-string
# }
# catch {
# Write-PSFMessage -Level Warning "Error, $_"
# Write-Host "Error, $_"
# throw $_
# }
# return $syntax #|convertto-json -Depth 10
# $json = Get-Clipboard

# $newLineCommaDelim = @"
# ,

# "@
# try {
# $copy2ClipBoardData = @()
# $copy2ClipBoardData += @"
# function New-FMObj$objectName {
# [CmdletBinding()]
# param (
# "@
# $defParameter = @()
# $defHashMap = @()
# $sourceHashTable = $json | ConvertFrom-Json -ErrorAction Stop | convertto-psfhashtable
# $sourceKeyList = $sourceHashTable.Keys | Sort-Object
# foreach ($sourceKey in $sourceKeyList) {
# $parameterName = [regex]::Replace($sourceKey.Trim('_'), '(?i)(?:^|-| )(\p{L})', { $args[0].Groups[1].Value.ToUpper() })
# $parameterType = $sourceHashTable.$sourceKey.gettype()
# $parameterValue = $sourceHashTable.$sourceKey
# Write-PSFMessage "`$parameterName=$parameterName; Type=$parameterType;value=$parameterValue"
# # $defParameter+=@"
# # [parameter(mandatory = `$false, ParameterSetName = "default")]
# # [$parameterType]`$$parameterName
# # "@
# # Hashtable Definition ergänzen
# switch ($parameterType) {
# "long" {
# $defParameter += @"
# [parameter(mandatory = `$false, ParameterSetName = "default")]
# [$parameterType]`$$parameterName=-1
# "@
# }
# Default {
# switch -regex ($parameterValue) {
# "disable|disable" {
# $defParameter += @"
# [parameter(mandatory = `$false, ParameterSetName = "default")]
# [ValidateSet("disable", "enable")]
# [$parameterType]`$$parameterName
# "@
# }
# Default {
# $defParameter += @"
# [parameter(mandatory = `$false, ParameterSetName = "default")]
# [$parameterType]`$$parameterName
# "@
# }
# }
# }
# }
# # Hashtable Definition ergänzen
# switch ($parameterType) {
# "System.Object[]" { $defHashMap += "'$sourceKey'=@(`$$parameterName)" }
# "string" { $defHashMap += "'$sourceKey'=`"`$$parameterName`"" }
# "long" { $defHashMap += "'$sourceKey'=`$$parameterName" }
# Default { Write-PSFMessage -Level Warning "Unknown ParamaterType $parameterType" }
# }
# }
# $defParameter += @"
# [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
# [parameter(mandatory = `$false, ValueFromPipeline = `$false, ParameterSetName = "default")]
# `$NullHandler = "RemoveAttribute"
# "@
# $copy2ClipBoardData += ($defParameter | Join-String $newLineCommaDelim)
# $copy2ClipBoardData += ")"
# $copy2ClipBoardData += "`$data=@{"
# $copy2ClipBoardData += ($defHashMap | Out-String)
# $copy2ClipBoardData += "}"
# $copy2ClipBoardData += "return `$data | Remove-FMNullValuesFromHashtable -NullHandler `$NullHandler"
# $copy2ClipBoardData += "}"
# Write-PSFMessage -Level Host ($copy2ClipBoardData | out-string)
# $copy2ClipBoardData | out-string | Set-Clipboard
# }
# catch {
# Write-PSFMessage -Level Warning "Clipboard did not contain a JSON String, $_"
# }
# }

function ConvertTo-CamelCase {
    <#
    .SYNOPSIS
    Converts attribute names into CamelCase parameter names.
 
    .DESCRIPTION
    Converts attribute names into CamelCase parameter names.
 
    .PARAMETER Text
    The text to be converted.
 
    .EXAMPLE
    ConvertTo-CamelCase -Text "first_hit"
 
    Returns FirstHit
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param (
        [Parameter(Mandatory = $true,Position = 0)]
        [string]$Text
    )
    return [regex]::Replace($Text.Trim('_').Trim(' '), '(?i)(?:^|-| )(\p{L})', { $args[0].Groups[1].Value.ToUpper() })
}

function Add-FMAddress {
    <#
    .SYNOPSIS
    Adds new addresses to the given ADOM.
 
    .DESCRIPTION
    Adds new addresses to the given ADOM.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Address
    The new address, generated e.g. by using New-FMObjAddress
 
    .PARAMETER Overwrite
    If used and an address with the given name already exists the data will be
    overwritten. Attention! If used and the new address lacks attributes which
    are already present in the table, this will result in a delta update.
 
      .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
    .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    # Read some input in the format [IP]/[Subnet-Mask]
    $missingAddresses=Get-Content "PATH TO SOME FILE"
    # prepare a temporary Array
    $newFMAddresses=@()
    # Fill the array with the needed structure
    foreach ($newAddress in $missingAddresses) {
        $newFMAddresses += New-FMObjAddress -Name "$newAddress" -Type ipmask -Subnet "$newAddress" -Comment "Created for testing purposes"
    }
    # Lock + Add + Commit + Unlock
    Lock-FMAdom -Connection $connection
    $newFMAddresses | Add-FMaddress -connection $connection
    Publish-FMAdomChange -Connection $connection
    UnLock-FMAdom -Connection $connection
 
    Read som subet information and add the subnets to the fortigate manager
    .NOTES
    General notes
    #>

    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        [object[]]$Address,
        [switch]$Overwrite,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $addressList = @()
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        $validAttributes = Get-PSFConfigValue -FullName 'FortigateManager.ValidAttr.FirewallAddress'
    }
    process {
        $Address | ForEach-Object { $addressList += $_ | ConvertTo-PSFHashtable -Include $validAttributes }
    }
    end {
        $apiCallParameter = @{
            EnableException     = $EnableException
            RevisionNote        = $RevisionNote
            Connection          = $Connection
            LoggingAction       = "Add-FMAddress"
            LoggingActionValues = @($addressList.count, $explicitADOM)
            method              = "add"
            Path                = "/pm/config/adom/$explicitADOM/obj/firewall/address"
            Parameter           = @{
                "data" = $addressList
            }
        }
        if ($Overwrite) {
            Write-PSFMessage "Existi n g data should be overwritten"
            $apiCallParameter.method = "set"
        }
        $result = Invoke-FMAPI @apiCallParameter
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }
}

function Add-FMAddressGroup {
    <#
    .SYNOPSIS
    Adds new address groups to the given ADOM.
 
    .DESCRIPTION
    Adds new address groups to the given ADOM.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER AddressGroup
    The new address group, generated e.g. by using New-FMObjAddressGroup
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .PARAMETER Overwrite
    If used and an address with the given name already exists the data will be overwritten.
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        [object[]]$AddressGroup,
        [switch]$Overwrite,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $groupList = @()
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        $validAttributes = Get-PSFConfigValue -FullName 'FortigateManager.ValidAttr.FirewallAddressGroups'
    }
    process {
        $AddressGroup | ForEach-Object { $groupList += $_ | ConvertTo-PSFHashtable -Include $validAttributes }
    }
    end {
        $apiCallParameter = @{
            RevisionNote        = $RevisionNote
            EnableException     = $EnableException
            Connection          = $Connection
            LoggingAction       = "Add-FMAddressGroup"
            LoggingActionValues = @($groupList.count, $explicitADOM)
            method              = "add"
            Path                = "/pm/config/adom/$explicitADOM/obj/firewall/addrgrp"
            Parameter           = @{
                "data" = $groupList
            }
        }
        if ($Overwrite) {
            Write-PSFMessage "Existing data should be overwritten"
            $apiCallParameter.method = "set"
        }
        $result = Invoke-FMAPI @apiCallParameter
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }
}

function Add-FMAdomRevision {
    <#
    .SYNOPSIS
    Creates a new revision of the given ADOM.
 
    .DESCRIPTION
    Creates a new revision of the given ADOM.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Name
    Name of the revision, mandatory
 
    .PARAMETER Desc
    Description of the revision
 
    .PARAMETER Locked
    Should the revision be protected against deletion?
 
    .PARAMETER NullHandler
    Parameter description
 
 
    .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    Add-FMAdomRevision -Name "Prior multiple deletion"
 
    Creates the new revision.
 
    .NOTES
    General notes
    #>

    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Desc,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [switch]$Locked,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [string]$Name,
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute",
        [bool]$EnableException = $true
    )
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
    Write-PSFMessage "`$explicitADOM=$explicitADOM"
    $apiCallParameter = @{
        EnableException     = $EnableException
        RevisionNote        = $RevisionNote
        Connection          = $Connection
        LoggingAction       = "Add-FMAdomRevision"
        LoggingActionValues = @($explicitADOM)
        method              = "add"
        Path                = "/dvmdb/adom/$explicitADOM/revision"
        Parameter           = @{
            data = @{
                'created_by' = $connection.AuthenticatedUser
                'desc'       = "$Desc"
                'locked'     = 0
                'name'       = "$Name"
            } | Remove-FMNullValuesFromHashtable -NullHandler $NullHandler
        }
    }
    if ($Locked) { $apiCallParameter.Parameter.data.locked = 1 }
    $result = Invoke-FMAPI @apiCallParameter
    if (-not $EnableException) {
        return ($null -ne $result)
    }
}

function Add-FMFirewallPolicy {
    <#
    .SYNOPSIS
    Adds new firewall policies to the given ADOM and policy package.
 
    .DESCRIPTION
    Adds new firewall policies to the given ADOM and policy package. This
    function adds the policy to the End of the Package. If you need it elsewhere
    you have to use ""
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Package
    The name of the policy package
 
    .PARAMETER Policy
    The new policy, generated e.g. by using New-FMObjAddress
 
    .PARAMETER After
    If used the policy will be added after the policy with the given ID
 
    .PARAMETER Before
    If used the policy will be added before the policy with the given ID
 
    .PARAMETER Overwrite
    If used and an policy with the given name already exists the data will be
    overwritten.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
 
    .EXAMPLE
    #To Be Provided
 
    Later
    .NOTES
    General notes
    #>

    param (
        [parameter( ParameterSetName = "default")]
        #[parameter( ParameterSetName = "undocumentedAfter")]
        #[parameter( ParameterSetName = "undocumentedBefore")]
        $Connection = (Get-FMLastConnection),
        [parameter( ParameterSetName = "default")]
        #[parameter( ParameterSetName = "undocumentedAfter")]
        #[parameter( ParameterSetName = "undocumentedBefore")]
        [string]$ADOM,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [parameter(mandatory = $true, ParameterSetName = "undocumentedAfter")]
        [parameter( mandatory = $true, ParameterSetName = "undocumentedBefore")]
        [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("FortigateManager.FirewallPackage")]
        [string]$Package,
        #[parameter( ParameterSetName = "undocumentedAfter")]
        [string]$After,
        #[parameter( ParameterSetName = "undocumentedBefore")]
        [string]$Before,
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        # [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "undocumentedAfter")]
        # [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "undocumentedBefore")]
        [object[]]$Policy,
        [parameter( ParameterSetName = "default")]
        #[parameter( ParameterSetName = "undocumentedAfter")]
        #[parameter( ParameterSetName = "undocumentedBefore")]
        [switch]$Overwrite,
        [parameter( ParameterSetName = "default")]
        #[parameter( ParameterSetName = "undocumentedAfter")]
        #[parameter( ParameterSetName = "undocumentedBefore")]
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $policyList = @()
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        $validAttributes = Get-PSFConfigValue -FullName 'FortigateManager.ValidAttr.FirewallPolicy'
    }
    process {
        $Policy | ForEach-Object {
            $policyList += $_ | ConvertTo-PSFHashtable -Include $validAttributes
        }
    }
    end {
        $apiCallParameter = @{
            EnableException     = $EnableException
            RevisionNote        = $RevisionNote
            Connection          = $Connection
            LoggingAction       = "Add-FMFirewallPolicy"
            LoggingActionValues = @($policyList.count, $explicitADOM, $Package)
            method              = "add"
            Path                = "/pm/config/adom/$explicitADOM/pkg/$Package/firewall/policy"
            Parameter           = @{
                "data" = $policyList
            }
        }
        if ($Overwrite) {
            Write-PSFMessage "Existing data should be overwritten"
            $apiCallParameter.method = "set"
        }
        if (-not [string]::IsNullOrEmpty($After)) {
            Write-PSFMessage "Using undocumented after parameter"
            $apiCallParameter.Parameter.after = "$after"
        }
        if (-not [string]::IsNullOrEmpty($Before)) {
            Write-PSFMessage "Using undocumented before parameter"
            $apiCallParameter.Parameter.before = "$Before"
        }
        $result = Invoke-FMAPI @apiCallParameter
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }
}


function Add-FMFirewallService {
    <#
    .SYNOPSIS
    Adds new firewall services to the given ADOM.
 
    .DESCRIPTION
    Adds new firewall services to the given ADOM.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Service
    The new service, generated e.g. by using New-FMObjFirewallService
 
    .PARAMETER Overwrite
    If used and an address with the given name already exists the data will be overwritten.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    # Read some input in the format [IP]/[Subnet-Mask]
    $missingAddresses=Get-Content "PATH TO SOME FILE"
    # prepare a temporary Array
    $newFMAddresses=@()
    # Fill the array with the needed structure
    foreach ($newAddress in $missingAddresses) {
        $newFMAddresses += New-FMObjAddress -Name "$newAddress" -Type ipmask -Subnet "$newAddress" -Comment "Created for testing purposes"
    }
    # Lock + Add + Commit + Unlock
    Lock-FMAdom -Connection $connection
    $newFMAddresses | Add-FMaddress -connection $connection
    Publish-FMAdomChange -Connection $connection
    UnLock-FMAdom -Connection $connection
 
    Read som subet information and add the subnets to the fortigate manager
    .NOTES
    General notes
    #>

    param (
        [parameter(Mandatory=$false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        [object[]]$Service,
        [switch]$Overwrite,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $serviceList = @()
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        $validAttributes = Get-PSFConfigValue -FullName 'FortigateManager.ValidAttr.FirewallService'
    }
    process {
        $Service | ForEach-Object { $serviceList += $_ | ConvertTo-PSFHashtable -Include $validAttributes }
    }
    end {
        $apiCallParameter = @{
            EnableException     = $EnableException
            RevisionNote        = $RevisionNote
            Connection          = $Connection
            LoggingAction       = "Add-FMFirewallService"
            LoggingActionValues = @($serviceList.count, $explicitADOM)
            method              = "add"
            Path                = "/pm/config/adom/$explicitADOM/obj/firewall/service/custom"
            Parameter           = @{
                "data" = $serviceList
            }
        }
        if ($Overwrite){
            Write-PSFMessage "Existing data should be overwritten"
            $apiCallParameter.method="set"
        }
        $result = Invoke-FMAPI @apiCallParameter
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }
}

function Add-FMInterface {
    <#
    .SYNOPSIS
    Adds new interfaces to the given ADOM.
 
    .DESCRIPTION
    Adds new interfaces to the given ADOM.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Interface
    The new interface, generated e.g. by using New-FMObjInterface
 
    .PARAMETER Overwrite
    If used and an Interface with the given name already exists the data will be
    overwritten. Attention! If used and the new Interface lacks attributes which
    are already present in the table, this will result in a delta update.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    To be written
 
    sometime
    .NOTES
    General notes
    #>

    param (
        [parameter(Mandatory=$false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        [object[]]$Interface,
        [switch]$Overwrite,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $InterfaceList = @()
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        $validAttributes = Get-PSFConfigValue -FullName 'FortigateManager.ValidAttr.FirewallInterface'
    }
    process {
        $Interface | ForEach-Object { $InterfaceList += $_ | ConvertTo-PSFHashtable -Include $validAttributes }
    }
    end {
        $apiCallParameter = @{
            EnableException     = $EnableException
            RevisionNote        = $RevisionNote
            Connection          = $Connection
            LoggingAction       = "Add-FMInterface"
            LoggingActionValues = @($InterfaceList.count, $explicitADOM)
            method              = "add"
            Path                = "/pm/config/adom/$explicitADOM/obj/dynamic/interface"
            Parameter           = @{
                "data" = $InterfaceList
            }
        }
        if ($Overwrite){
            Write-PSFMessage "Existing data should be overwritten"
            $apiCallParameter.method="set"
        }
        $result = Invoke-FMAPI @apiCallParameter
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }
}

function Connect-FM {
    <#
    .SYNOPSIS
    Creates a new Connection Object to a Fortigate Manager instance.
 
    .DESCRIPTION
    Creates a new Connection Object to a Fortigate Manager instance.
 
    .PARAMETER Credential
    Credential-Object for direct login.
 
    .PARAMETER Url
    The server root URL.
 
    .PARAMETER ADOM
    The default ADOM for the requests.
 
    .PARAMETER SkipCheck
    Array of checks which should be skipped while using Invoke-WebRequest.
    Possible Values 'CertificateCheck', 'HttpErrorCheck', 'HeaderValidation'.
    If neccessary by default for the connection set $connection.SkipCheck
 
    .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    $connection=Connect-FM -Url $url -Credential $cred
 
    Connect directly with a Credential-Object
 
    .NOTES
    #>


    # [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '')]
    # [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
    [CmdletBinding(DefaultParameterSetName = "credential")]
    Param (
        [parameter(mandatory = $true, ParameterSetName = "credential")]
        [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("FM.url")]
        [string]$Url,
        [parameter(mandatory = $false, ParameterSetName = "credential")]
        [string]$ADOM,
        [parameter(mandatory = $true, ParameterSetName = "credential")]
        [pscredential]$Credential,
        [ValidateSet('CertificateCheck', 'HttpErrorCheck', 'HeaderValidation')]
        [String[]]$SkipCheck,
        [bool]$EnableException = $true
    )
    begin {
    }
    end {
        $connection = Get-ARAHConnection -Url $Url -APISubPath ""
        if ($SkipCheck) { $connection.SkipCheck = $SkipCheck}
        Add-Member -InputObject $connection -MemberType NoteProperty -Name "forti" -Value @{
            requestId = 1
            session   = $null
            EnableException=$EnableException
        }
        $connection.credential = $Credential
        $connection.ContentType = "application/json;charset=UTF-8"
        $connection.authenticatedUser = $Credential.UserName
        if ($ADOM) {
            $connection.forti.defaultADOM = $ADOM
        }

        Add-Member -InputObject $connection -MemberType ScriptMethod -Name "Refresh" -Value {
            $functionName = "Connect-FM>Refresh"
            Write-PSFMessage "Stelle Verbindung her zu $($this.ServerRoot)" -Target $this.ServerRoot -FunctionName $functionName

            $apiCallParameter = @{
                Connection          = $this
                EnableException     = $this.forti.EnableException
                method              = "exec"
                Path                = "sys/login/user"
                LoggingAction       = "Connect-FM"
                LoggingActionValues = @($this.ServerRoot, $this.Credential.UserName)
                Parameter           = @{
                    "data" = @{
                        "passwd" = $this.Credential.GetNetworkCredential().Password
                        "user"   = $this.Credential.UserName
                    }
                }
            }

            # Invoke-PSFProtectedCommand -ActionString "Connect-FM.Connecting" -ActionStringValues $Url -Target $Url -ScriptBlock {
            $result = Invoke-FMAPI @apiCallParameter
            if ($null -eq $result) {
                Stop-PSFFunction -Message "No API Results" -EnableException $EnableException -FunctionName $functionName
            }
            # } -PSCmdlet $PSCmdlet -EnableException $EnableException
            if (Test-PSFFunctionInterrupt) {
                Write-PSFMessage "Test-PSFFunctionInterrupt" -FunctionName $functionName
                return
            }
            if ($result.session) {
                $this.forti.session = $result.session
            }
        }
        $connection.Refresh()
        if ($connection.forti.session) {
            Write-PSFMessage -string "Connect-FM.Connected"
            Set-PSFConfig -Module 'FortigateManager' -Name 'LastConnection' -Value $connection -Description "Last known Connection" -AllowDelete
            return $connection
        }
        Write-PSFMessage -string "Connect-FM.NotConnected" -Level Warning
    }
}

function Convert-FMIpAddressToMaskLength{
    <#
    .SYNOPSIS
    Converts a IP Subnet-Mask to a Mask Length
 
    .DESCRIPTION
    Converts a IP Address to a Mask Length
 
    .PARAMETER dottedIpAddressString
    The input IP
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
 
    .EXAMPLE
    Convert-FMIpAddressToMaskLength -dottedIpAddressString "255.255.255.0"
 
    Returns 24
 
    .EXAMPLE
    Convert-FMIpAddressToMaskLength -dottedIpAddressString "255.255.255.255"
 
    Returns 32
 
    .NOTES
    General notes
    #>

    [OutputType([string])]
    param(
        [string] $dottedIpAddressString
        )

    $result = 0;
    try {
        # ensure we have a valid IP address
        [IPAddress] $ip = $dottedIpAddressString;
        $octets = $ip.IPAddressToString.Split('.');
        foreach ($octet in $octets) {
            while (0 -ne $octet) {
                $octet = ($octet -shl 1) -band [byte]::MaxValue
                $result++;
            }
        }
    }
    catch {
        Write-PSFMessage -Level Warning "No valid IP Mask"
    }
    return $result;
}

function Convert-FMSubnetMask {
    <#
    .SYNOPSIS
    Converts a subnet mask between long and short-version
 
    .DESCRIPTION
    Long description
 
    .PARAMETER Target
    What is the desired return format?
    CIDR: single digit, 1-32
    Octet: xxx.xxx.xxx.xxx
    Auto: Convert depending on the current format, CIDR->Octect and revers
 
    .PARAMETER SubnetMask
    The current subnet mask which should be converted
 
    .PARAMETER IPMask
    The current ip/subnetmask which should be converted
 
    .PARAMETER EnableException
    Should Exceptions been thrown? If the SubnetMask can't be converted and set
    to $true an exception will be thrown, otherwise the function returns $null
 
    .EXAMPLE
    Convert-FMSubnetMask -SubnetMask 255.255.255.255
 
    Returns 32
 
    .EXAMPLE
    Convert-FMSubnetMask -SubnetMask 24
 
    Returns 255.255.255.0
    .EXAMPLE
    Convert-FMSubnetMask -IPMask "192.16.0.0/255.255.255.0"
 
    Returns 192.16.0.0/24
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param (
        # [parameter(mandatory = $true, ParameterSetName = "cidr")]
        # [string]$Cidr,
        [parameter(mandatory = $true, ParameterSetName = "subnet")]
        [string]$SubnetMask,
        [parameter(mandatory = $true, ParameterSetName = "ipMask")]
        [string]$IPMask,
        [ValidateSet("Auto", "CIDR", "Octet")]
        [string]$Target="Auto",
        [bool]$EnableException = $true
    )

    begin {
        $conversionTable = @{
            "255.128.0.0"     = "9"
            "255.255.224.0"   = "19"
            "255.255.255.240" = "28"
            "12"              = "255.240.0.0"
            "27"              = "255.255.255.224"
            "4"               = "240.0.0.0"
            "32"              = "255.255.255.255"
            "6"               = "252.0.0.0"
            "2"               = "192.0.0.0"
            "20"              = "255.255.240.0"
            "29"              = "255.255.255.248"
            "8"               = "255.0.0.0"
            "23"              = "255.255.254.0"
            "255.248.0.0"     = "13"
            "3"               = "224.0.0.0"
            "255.255.248.0"   = "21"
            "255.255.240.0"   = "20"
            "16"              = "255.255.0.0"
            "18"              = "255.255.192.0"
            "255.255.192.0"   = "18"
            "128.0.0.0"       = "1"
            "22"              = "255.255.252.0"
            "255.255.255.128" = "25"
            "255.192.0.0"     = "10"
            "255.252.0.0"     = "14"
            "255.224.0.0"     = "11"
            "255.254.0.0"     = "15"
            "19"              = "255.255.224.0"
            "255.240.0.0"     = "12"
            "14"              = "255.252.0.0"
            "192.0.0.0"       = "2"
            "7"               = "254.0.0.0"
            "31"              = "255.255.255.254"
            "28"              = "255.255.255.240"
            "21"              = "255.255.248.0"
            "10"              = "255.192.0.0"
            "254.0.0.0"       = "7"
            "240.0.0.0"       = "4"
            "11"              = "255.224.0.0"
            "255.255.255.252" = "30"
            "1"               = "128.0.0.0"
            "255.255.128.0"   = "17"
            "255.0.0.0"       = "8"
            "17"              = "255.255.128.0"
            "26"              = "255.255.255.192"
            "30"              = "255.255.255.252"
            "248.0.0.0"       = "5"
            "5"               = "248.0.0.0"
            "255.255.255.248" = "29"
            "15"              = "255.254.0.0"
            "255.255.252.0"   = "22"
            "255.255.255.254" = "31"
            "9"               = "255.128.0.0"
            "255.255.255.192" = "26"
            "252.0.0.0"       = "6"
            "25"              = "255.255.255.128"
            "13"              = "255.248.0.0"
            "24"              = "255.255.255.0"
            "255.255.255.255" = "32"
            "224.0.0.0"       = "3"
            "255.255.255.0"   = "24"
            "255.255.255.224" = "27"
            "255.255.0.0"     = "16"
            "255.255.254.0"   = "23"
        }
    }

    end {
        if($IPMask){
            Write-PSFMessage "Splitting ipMask into ip and subnet: $IPMask"
            $pair=$IPMask -split '/'
            $ip = $pair[0]
            $SubnetMask = $pair[1]
            Write-PSFMessage "Splitting ipMask into ip ($($pair[0]))and subnet $($pair[1]): $IPMask"
        }
        if($SubnetMask.length -gt 2){
            $currentFormat= "Octet"
        }else{
            $currentFormat= "CIDR"
        }
        Write-PSFMessage "Converting $currentFormat>$Target"
        if($currentFormat -eq $Target){
            Write-PSFMessage "No Conversion of $SubnetMask needed"
            $result="$SubnetMask"
        }else{
            $result = $conversionTable."$SubnetMask"
        }
        if($null -eq $result -and $EnableException){
            throw "Could not convert subnet mask $SubnetMask"
        }
        Write-PSFMessage "`$result=$result"

        if($IPMask){
            return "$ip/$result"
        }
        return $result
    }
}


function Convert-FMZone2VLAN {
    <#
    .SYNOPSIS
    Converts an interface from a firewall policy to the addresses of the physical interfaces/vlan on the relevant devices.
 
    .DESCRIPTION
    Converts an interface from a firewall policy to the addresses of the physical interfaces/vlan on the relevant devices.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER LoggingLevel
    On which level should die diagnostic Messages be logged?
 
    .PARAMETER EnableException
    If set to True, errors will throw an exception
 
    .PARAMETER Zone
    The names of the interface/Zone/localiced interface name (found in policy rules)
 
    .PARAMETER ReturnType
    How should the results be returned?
    'simpleIpList': Array of ipmasks
    'ZoneVLANHash': Hashtable, @{"$zoneName"=[Array of ipmasks]}
    'ZoneVDOMVLANHash': Hashtable,
        values=[Array of ipmasks]
        keys= "{ZONE-Name}|{VDOM}"
 
    .PARAMETER Scope
    The scope which should be looked up.
    @(@{name='deviceName';vdom='vdom name'})
 
    By default all available devices/vdoms will be looked up.
 
    .EXAMPLE
     $policy=Get-FMFirewallPolicy -Package ALL -Option 'scope member' -filter "policyid -eq 1234"
     Convert-FMZone2VLAN -Zone $policy.srcintf,$policy.dstintf
 
     Returns a list of ipmasks
 
    .EXAMPLE
     $policy=Get-FMFirewallPolicy -Package ALL -Option 'scope member' -filter "policyid -eq 1234"
     Convert-FMZone2VLAN -Zone $policy.srcintf,$policy.dstintf
 
     Returns a list of ipmasks
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [parameter(Mandatory = $true)]
        [string[]]$Zone,
        [ValidateSet('simpleIpList', 'ZoneVLANHash', 'ZoneVDOMVLANHash')]
        $ReturnType = 'simpleIpList',
        [string]$LoggingLevel='Verbose',
        $Scope,
        [bool]$EnableException = $true
    )
    Write-PSFMessage "Determine VLANs from Zones: $($Zone -join ',')"
    Write-PSFMessage "Query all VDOMs and corresponding VLANs"
    if ($null -eq $Scope) {
        Write-PSFMessage "Scope is ALL devices and vdoms"
        # $Scope = Get-FMDeviceInfo -Connection $connection -Option 'object member' | Select-Object -ExpandProperty "object member" | Where-Object { $_.vdom } | ConvertTo-PSFHashtable -Include name, vdom
        $Scope = Get-FMFirewallScope -Connection $Connection | ConvertTo-PSFHashtable -Include name, vdom
    }else{
        $Scope = $Scope|ConvertTo-PSFHashtable
    }
    # Query all localized interface names into a HashTable
    # Key-Format: [Localized Interface Name]|[Device Name]|[Device VDOM]
    # Value: List of local Zone/Interface Names
    $localizedInterfaceList = Get-FMInterface
    $localizedInterfaceHashMap = @{}
    foreach ($interface in $localizedInterfaceList) {
        $localizedName = $interface.name
        foreach ($dynaMapping in $interface.dynamic_mapping) {
            foreach ($interfaceScope in $dynaMapping._scope) {
                $key = "$localizedName|$($interfaceScope.name)|$($interfaceScope.vdom)"
                $localizedInterfaceHashMap.$key = $dynaMapping."local-intf"
            }
        }
    }
    # $Scope = Get-FMDeviceInfo -Connection $connection -Option 'object member' | Select-Object -ExpandProperty "object member" | Where-Object { $_.vdom } | ConvertTo-PSFHashtable -Include name, vdom -Remap @{name = 'device' }
    foreach ($device in $Scope) {
        Write-PSFMessage "Query Device $($device|ConvertTo-Json -Compress)"
        $apiCallParameter = @{
            EnableException     = $EnableException
            LoggingAction       = "Undocumented"
            Connection          = $Connection
            LoggingActionValues = "Query all interface VLAN from a specific Device/VDOM"
            method              = "get"
            LoggingLevel        = $LoggingLevel
            path                = "/pm/config/device/{name}/vdom/{vdom}/system/interface" | Merge-FMStringHashMap -Data $device
        }
        $device.vlanHash = @{}

        $currentInterfaces = Invoke-FMAPI @apiCallParameter # | ConvertTo-Json -Depth 6
        foreach ($interface in $currentInterfaces.result.data) {
            $name = $interface.name
            $device.vlanHash.$name = $interface.ip
            # if ($interface.ip[1] -eq '0.0.0.0') {
            # $address = "$($interface.ip[0])/0"
            # }
            # else {
            # $address = Convert-FMSubnetMask -IPMask "$($interface.ip[0])/$($interface.ip[1])"
            # }
            # $device.vlanHash.$name = $address
        }
    }
    switch -regex ($ReturnType) {
        '.*List' {
            $returnValue = @()
        }
        '.*Hash' {
            $returnValue = @{}
        }
    }
    Write-PSFMessage "`$returnValue.type=$($returnValue.GetType())"
    foreach ($curZone in $Zone) {
        Write-PSFMessage "Checking Zone: $curZone"
        switch ($ReturnType) {
            'ZoneVLANHash' { $returnValue.$curZone = @() }
        }
        $queryData = @{zone = $curZone | convertto-fmurlpart }
        $apiCallParameter = @{
            EnableException     = $true
            Connection          = $Connection
            LoggingAction       = "Undocumented"
            LoggingActionValues = "Query interfaces from a specific Device/VDOM"
            method              = "get"
            LoggingLevel        = 'Verbose'
        }
        $singleDeviceVdomURL = "/pm/config/device/{{name}}/vdom/{{vdom}}/system/zone/{zone}"
        foreach ($queryData in $Scope) {
            $hashKey = "$curZone|$($queryData.name)|$($queryData.vdom)"
            Write-PSFMessage "`$hashKey=$hashKey"
            $localZoneName = $localizedInterfaceHashMap.$hashKey
            Write-PSFMessage "Local Interface Name from localized Name $curZone=$localZoneName"
            if ([string]::IsNullOrEmpty($localZoneName)) {
                Write-PSFMessage "No local name found, continue"
                continue
            }
            $queryData.zone = $localZoneName | convertto-fmurlpart
            $apiCallParameter.path = $singleDeviceVdomURL | Merge-FMStringHashMap -Data $queryData
            Write-PSFMessage "`$queryData=$($queryData|ConvertTo-Json -compress), Path=$($apiCallParameter.path)"
            try {
                $result = Invoke-FMAPI @apiCallParameter
                Write-PSFMessage "Found"
                $interfaceList = $result.result.data.interface
                # Write-PSFMessage "Query: $($queryData|ConvertTo-Json -compress), Results: $($result|ConvertTo-Json -Depth 4)"
                Write-PSFMessage "interfaceList for $($queryData|ConvertTo-Json -compress): $($interfaceList -join ',')"
                foreach ($interface in $interfaceList) {
                    $vlanIP = $queryData.vlanHash.$interface
                    if ($vlanIP[1] -eq '0.0.0.0') {
                        $address = "$($vlanIP[0])/0"
                    }
                    else {
                        $address = Convert-FMSubnetMask -IPMask "$($vlanIP[0])/$($vlanIP[1])" -Verbose:$false
                    }

                    switch ($ReturnType) {
                        'simpleIpList' {
                            Write-PSFMessage "Adding VLAN Info to simpleIpListList"
                            $returnValue += $address
                        }
                        'ZoneVLANHash' {
                            Write-PSFMessage "Adding Interfaces to ZoneVLANHash"
                            $returnValue.$curZone += $address
                        }
                        'ZoneVDOMVLANHash' {
                            $returnKey = "$curZone|$($queryData.vdom)"
                            Write-PSFMessage "returnKey=$returnKey" -Tag "hubba"
                            $returnValue.$returnKey = $address
                        }
                        default {
                            Write-PSFMessage "`$ReturnType $ReturnType unknown"
                        }
                    }
                }

            }
            catch {
                Write-PSFMessage "Zone does not exist for $($queryData|Select-Object name,vdom|ConvertTo-Json -Compress)"
            }
        }
    }
    # Write-PSFMessage "`$returnValue=$($returnValue|ConvertTo-Json)" # $localizedInterfaceHashMap | json
    return $returnValue
}

function Disable-FMFirewallPolicy {
    <#
    .SYNOPSIS
    Disables specific firewall policies in the given ADOM and policy package.
 
    .DESCRIPTION
    Disables specific firewall policies in the given ADOM and policy package.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Package
    The name of the policy package
 
    .PARAMETER PolicyID
    The policyid attribut of the policy to modify.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    Disable-FMFirewallPolicy -Package $packageName -PolicyID 4711,4712
 
    Disables the two policies.
 
    .EXAMPLE
    4711,4712 | Disable-FMFirewallPolicy -Package $packageName
 
    Disables the two policies.
 
    .EXAMPLE
    $newPolicies = Get-FMFirewallPolicy -Package $packageName -Filter "name -like PESTER policy B-%$pesterGUID"
    $newPolicies | Disable-FMFirewallPolicy -Package $packageName
 
    Disables the returned policies.
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ParameterSetName = "multiUpdate")]
        [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("FortigateManager.FirewallPackage")]
        [string]$Package,
        [parameter(mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "multiUpdate")]
        [Int64[]]$PolicyID,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        $attributesToModify = @{status = 'disable' }
        $policyIdList = @()
    }
    process {
        $PolicyID | ForEach-Object {
                $policyIdList += $_
        }
    }
    end {
        Write-PSFMessage "Disabling Policies $($policyIdList|Join-String ',')"
        return Update-FMFirewallPolicy -Connection $Connection -Adom $explicitADOM -Package $Package -PolicyId $policyIdList -Attribute $attributesToModify -EnableException $EnableException -RevisionNote $RevisionNote
    }
}

function Disconnect-FM {
    <#
    .SYNOPSIS
    Disconnects from an existing connection
 
    .DESCRIPTION
    Disconnects from an existing connection
 
    .PARAMETER Connection
    The API connection object.
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
 
    .EXAMPLE
    To be added
 
    in the Future
 
    .NOTES
    General notes
    #>

    param (
        [parameter(Mandatory=$false)]
        $Connection = (Get-FMLastConnection),
        [bool]$EnableException = $true
    )
    $apiCallParameter = @{
        EnableException = $EnableException
        Connection      = $Connection
        LoggingAction       = "Disconnect-FM"
        LoggingActionValues = ""
        method          = "exec"
        Path            = "sys/logout"
    }
    $lastConnection=Get-FMLastConnection -EnableException $EnableException
    $result=Invoke-FMAPI @apiCallParameter
    if ($lastConnection -and $lastConnection.forti.session -eq $Connection.forti.session){
        Write-PSFMessage "Remove stored last connection"
        Remove-PSFConfig -FullName 'FortigateManager.LastConnection' -Confirm:$false
    }
    if (-not $EnableException) {
        return ($null -ne $result)
    }
}

function Enable-FMFirewallPolicy {
    <#
    .SYNOPSIS
    Enables specific firewall policies in the given ADOM and policy package.
 
    .DESCRIPTION
    Enables specific firewall policies in the given ADOM and policy package.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Package
    The name of the policy package
 
    .PARAMETER PolicyID
    The policyid attribut of the policy to modify.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    Enable-FMFirewallPolicy -Package $packageName -PolicyID 4711,4712
 
    Enables the two policies.
 
    .EXAMPLE
    4711,4712 | Enable-FMFirewallPolicy -Package $packageName
 
    Enables the two policies.
 
    .EXAMPLE
    $newPolicies = Get-FMFirewallPolicy -Package $packageName -Filter "name -like PESTER policy B-%$pesterGUID"
    $newPolicies | Enable-FMFirewallPolicy -Package $packageName
 
    Enables the returned policies.
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ParameterSetName = "multiUpdate")]
        [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("FortigateManager.FirewallPackage")]
        [string]$Package,
        [parameter(mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "multiUpdate")]
        [Int64[]]$PolicyID,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        $attributesToModify = @{status = 'enable' }
        $policyIdList = @()
    }
    process {
        $PolicyID | ForEach-Object {
            $policyIdList += $_
        }
    }
    end {
        Write-PSFMessage "Enabling Policies $($policyIdList|Join-String ',')"
        return Update-FMFirewallPolicy -Connection $Connection -Adom $explicitADOM -Package $Package -PolicyId $policyIdList -Attribute $attributesToModify -EnableException $EnableException -RevisionNote $RevisionNote
    }
}

function Get-FMAddress {
    <#
    .SYNOPSIS
    Querys existing addresses.
 
    .DESCRIPTION
    Querys existing addresses.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER EnableException
    If set to True, errors will throw an exception
 
    .PARAMETER Attr
    The name of the attribute to retrieve its datasource. Only used with datasrc option.
 
    .PARAMETER Sortings
    Specify the sorting of the returned result.
 
    .PARAMETER Loadsub
    Enable or disable the return of any sub-objects. If not specified, the default is to return all sub-objects.
 
    .PARAMETER Option
    Set fetch option for the request. If no option is specified, by default the attributes of the objects will be returned.
    count - Return the number of matching entries instead of the actual entry data.
    scope member - Return a list of scope members along with other attributes.
    datasrc - Return all objects that can be referenced by an attribute. Require attr parameter.
    get reserved - Also return reserved objects in the result.
    syntax - Return the attribute syntax of a table or an object, instead of the actual entry data. All filter parameters will be ignored.
 
    .PARAMETER Filter
    Filter the result according to a set of criteria. For detailed help
    see about_FortigateManagerFilter
 
    .PARAMETER Name
    Shortcut for -Filter "name -eq $Name"
 
    .PARAMETER GetUsed
    Parameter description
 
    .PARAMETER Range
    Limit the number of output. For a range of [a, n], the output will contain n elements, starting from the ath matching result.
 
    .PARAMETER Fields
    Limit the output by returning only the attributes specified in the string array. If none specified, all attributes will be returned.
 
    .PARAMETER LoggingLevel
    On which level should die diagnostic Messages be logged?
 
    .PARAMETER NullHandler
    Parameter description
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [bool]$EnableException = $true,

        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Attr,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Sortings,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Loadsub = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("count", "scope member", "datasrc", "get reserved", "syntax")]
        [string]$Option,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string[]]$Filter,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$GetUsed = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Range,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("_image-base64", "allow-routing", "associated-interface", "cache-ttl", "clearpass-spt", "color", "comment", "country", "dirty", "end-ip", "epg-name", "fabric-object", "filter", "fqdn", "fsso-group", "interface", "macaddr", "name", "node-ip-only", "obj-id", "obj-tag", "obj-type", "organization", "policy-group", "sdn", "sdn-addr-type", "sdn-tag", "start-ip", "sub-type", "subnet", "subnet-name", "tag-detection-level", "tag-type", "tenant", "type", "uuid", "wildcard", "wildcard-fqdn")]
        [System.Object[]]$Fields,
        [parameter(mandatory = $false, ParameterSetName = "default", Position = 0)]
        [String]$Name,
        [string]$LoggingLevel,
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute"
    )
    if ($Name) {
        If ($Filter) {
            $Filter += , "name -eq $Name"
        }
        else {
            $Filter = "name -eq $Name"
        }
    }
    $Parameter = @{
        'attr'     = "$Attr"
        'sortings' = @($Sortings)
        'loadsub'  = $Loadsub
        'option'   = "$Option"
        'filter'   = ($Filter | ConvertTo-FMFilterArray)
        'get used' = $GetUsed
        'range'    = @($Range)
        'fields'   = @($Fields)
    } | Remove-FMNullValuesFromHashtable -NullHandler $NullHandler
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM -EnableException $EnableException
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Get-FMAddress"
        LoggingActionValues = ($Parameter.Keys.Count)
        method              = "get"
        Parameter           = $Parameter
        Path                = "/pm/config/adom/$explicitADOM/obj/firewall/address"
    }
    if (-not [string]::IsNullOrEmpty($LoggingLevel)) { $apiCallParameter.LoggingLevel = $LoggingLevel }

    $result = Invoke-FMAPI @apiCallParameter
    Write-PSFMessage "Result-Status: $($result.result.status)"
    return $result.result.data
}


function Get-FMAddressGroup {
    <#
    .SYNOPSIS
    Query the addressgroups of the given ADOM.
 
    .DESCRIPTION
    Query the addressgroups of the given ADOM.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Attr
    The name of the attribute to retrieve its datasource. Only used with datasrc option.
 
    .PARAMETER Fields
    Limit the output by returning only the attributes specified in the string array. If none specified, all attributes will be returned.
 
    .PARAMETER Filter
    Filter the result according to a set of criteria. For detailed help
    see about_FortigateManagerFilter
 
    .PARAMETER Name
    Shortcut for -Filter "name -eq $Name"
 
    .PARAMETER GetUsed
    Parameter description
 
    .PARAMETER Loadsub
    Enable or disable the return of any sub-objects. If not specified, the default is to return all sub-objects.
 
    .PARAMETER Option
        Set fetch option for the request. If no option is specified, by default the attributes of the objects will be returned.
    count - Return the number of matching entries instead of the actual entry data.
    scope member - Return a list of scope members along with other attributes.
    datasrc - Return all objects that can be referenced by an attribute. Require attr parameter.
    get reserved - Also return reserved objects in the result.
    syntax - Return the attribute syntax of a table or an object, instead of the actual entry data. All filter parameters will be ignored.
 
 
    .PARAMETER Range
    Limit the number of output. For a range of [a, n], the output will contain n elements, starting from the ath matching result.
 
    .PARAMETER Sortings
    Specify the sorting of the returned result.
 
    .PARAMETER NullHandler
    Parameter description
 
    .PARAMETER LoggingLevel
    On which level should die diagnostic Messages be logged?
 
 
    .PARAMETER EnableException
    If set to True, errors will throw an exception
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,

        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Attr,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("_image-base64", "allow-routing", "category", "color", "comment", "exclude", "exclude-member", "fabric-object", "member", "name", "type", "uuid")]
        [System.Object[]]$Fields,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Filter,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$GetUsed = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Loadsub = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("count", "scope member", "datasrc", "get reserved", "syntax")]
        [string]$Option,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Range,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Sortings,
        [parameter(mandatory = $false, ParameterSetName = "default", Position = 0)]
        [String]$Name,
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute",
        [string]$LoggingLevel,
        [bool]$EnableException = $true
    )
    if ($Name) {
        If ($Filter) {
            $Filter += , "name -eq $Name"
        }
        else {
            $Filter = "name -eq $Name"
        }
    }

    $Parameter = @{
        'attr'     = "$Attr"
        'fields'   = @($Fields)
        'filter'   = ($Filter | ConvertTo-FMFilterArray)
        'get used' = $GetUsed
        'loadsub'  = $Loadsub
        'option'   = "$Option"
        'range'    = @($Range)
        'sortings' = @($Sortings)
    } | Remove-FMNullValuesFromHashtable -NullHandler $NullHandler
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM -EnableException $EnableException
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Get-FMAddressGroup"
        LoggingActionValues = ($Parameter.Keys.Count)
        method              = "get"
        Parameter           = $Parameter
        Path                = "/pm/config/adom/$explicitADOM/obj/firewall/addrgrp"
    }
    if (-not [string]::IsNullOrEmpty($LoggingLevel)) { $apiCallParameter.LoggingLevel = $LoggingLevel }

    $result = Invoke-FMAPI @apiCallParameter
    Write-PSFMessage "Result-Status: $($result.result.status)"
    return $result.result.data

}

function Get-FMAdomLockStatus {
    <#
    .SYNOPSIS
    Query the lockstatus of the given ADOM.
 
    .DESCRIPTION
    Query the lockstatus of the given ADOM.
    Returns null if not locked, otherwise detailed information is returned.
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .EXAMPLE
    To be added
 
    in the Future
 
    .NOTES
    General notes
    #>

    param (
        [parameter(Mandatory=$false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [bool]$EnableException = $true
    )
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM -EnableException $EnableException
    Write-PSFMessage "`$explicitADOM=$explicitADOM"
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Get-FMAdomLockStatus"
        LoggingActionValues = $explicitADOM
        method              = "get"
        Path                = "/dvmdb/adom/$explicitADOM/workspace/lockinfo"
    }

    $result = Invoke-FMAPI @apiCallParameter
    return $result.result.data
}

function Get-FMAdomRevision {
    <#
    .SYNOPSIS
    Querys existing ADOM Revisions.
 
    .DESCRIPTION
    Querys existing ADOM Revisions.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER EnableException
    If set to True, errors will throw an exception
 
    .PARAMETER Sortings
    Specify the sorting of the returned result.
 
    .PARAMETER Loadsub
    Enable or disable the return of any sub-objects. If not specified, the default is to return all sub-objects.
 
    .PARAMETER Option
    Set fetch option for the request. If no option is specified, by default the attributes of the objects will be returned.
    count - Return the number of matching entries instead of the actual entry data.
    scope member - Return a list of scope members along with other attributes.
    datasrc - Return all objects that can be referenced by an attribute. Require attr parameter.
    get reserved - Also return reserved objects in the result.
    syntax - Return the attribute syntax of a table or an object, instead of the actual entry data. All filter parameters will be ignored.
 
    .PARAMETER ExpandMembers
    Fetch all or selected attributes of object members.
    string($expand member object)
 
    .PARAMETER Filter
    Filter the result according to a set of criteria. For detailed help
    see about_FortigateManagerFilter
 
    .PARAMETER Range
    Limit the number of output. For a range of [a, n], the output will contain n elements, starting from the ath matching result.
 
    .PARAMETER Fields
    Limit the output by returning only the attributes specified in the string array. If none specified, all attributes will be returned.
 
    .PARAMETER LoggingLevel
    On which level should die diagnostic Messages be logged?
 
    .PARAMETER NullHandler
    Parameter description
 
    .EXAMPLE
    Get-FMAdomRevision
 
    Lists the existing Adom Revisions
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [bool]$EnableException = $true,

        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Sortings,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("count", "object member", "syntax")]
        [string]$Option,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ExpandMembers,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string[]]$Filter,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Range,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("created_by", "created_time", "desc", "locked", "name", "version")]
        [System.Object[]]$Fields,
        [string]$LoggingLevel,
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute"
    )
    $Parameter = @{
        'sortings' = @($Sortings)
        'option'   = "$Option"
        'filter'   = ($Filter | ConvertTo-FMFilterArray)
        'range'    = @($Range)
        'fields'   = @($Fields)
        'expand members'="$ExpandMembers"
    } | Remove-FMNullValuesFromHashtable -NullHandler $NullHandler
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM -EnableException $EnableException
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Get-FMAdomRevision"
        LoggingActionValues = $explicitADOM
        method              = "get"
        Parameter           = $Parameter
        Path                = "/dvmdb/adom/$explicitADOM/revision"
    }
    if (-not [string]::IsNullOrEmpty($LoggingLevel)) { $apiCallParameter.LoggingLevel = $LoggingLevel }

    $result = Invoke-FMAPI @apiCallParameter
    Write-PSFMessage "Result-Status: $($result.result.status)"
    return $result.result.data
}


function Get-FMDeviceInfo {
    <#
    .SYNOPSIS
    Querys Device Information.
 
    .DESCRIPTION
    Querys Device Information.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER EnableException
    If set to True, errors will throw an exception
 
    .PARAMETER Option
    Set fetch option for the request. If no option is specified, by default the attributes of the object will be returned.
    object member - Return a list of object members along with other attributes.
    chksum - Return the check-sum value instead of attributes.
 
    .PARAMETER NoAdom
    If used, no ADOM will be used and all ADOMs will be queried.
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [bool]$EnableException = $true,

        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("object member", "chksum")]
        [string]$Option,
        [switch]$NoADOM
    )
    $Parameter = @{
        'option' = "$Option"
    } | Remove-FMNullValuesFromHashtable -NullHandler "RemoveAttribute"
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM -EnableException $EnableException
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Get-FMDeviceInfo"
        LoggingActionValues = $explicitADOM
        method              = "get"
        Parameter           = $Parameter
        Path                = "/dvmdb/adom/$explicitADOM"
    }
    if ($NoADOM) {
        $apiCallParameter.Path = "/dvmdb/adom"
        $apiCallParameter.LoggingActionValues ="noAdom"
    }
    $result = Invoke-FMAPI @apiCallParameter
    Write-PSFMessage "Result-Status: $($result.result.status)"
    return $result.result.data
}


function Get-FMFirewallHitCount {
    <#
    .SYNOPSIS
    Queries hitcounts for a firewall policy.
 
    .DESCRIPTION
    Queries hitcounts for a firewall policy.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER EnableException
    If set to True, errors will throw an exception
 
    .PARAMETER Package
    The name of the policy package
 
    .PARAMETER Force
    If set, the hitcounts will be refreshed before query.
 
    .EXAMPLE
    $hitCountData=Get-FMFirewallHitCount -Package $packageName
    Write-Host "`$hitCountData.count=$($hitCountData."firewall policy".count)"
 
    Gets the hitcounts for the firewall policy
    .NOTES
    #>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [bool]$EnableException = $true,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [string]$Package,
        [switch]$Force
    )
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM -EnableException $EnableException
    $packageInfo = Get-FMPolicyPackage -Connection $Connection  -Adom $explicitADOM -Name $Package
    if ($Force -or $packageInfo."package settings"."hitc-taskid" -eq 0) {
        Write-PSFMessage -Level Host "Refresh hitcounts"
        $apiCallParameter = @{
            EnableException     = $EnableException
            Connection          = $Connection
            LoggingAction       = "Get-FMFirewallHitCount"
            LoggingActionValues = @($explicitADOM, $Package)
            method              = "exec"
            Parameter           = @{
                data = @{
                    adom = "$explicitADOM"
                    pkg  = "$Package"
                }
            }
            Path                = "/sys/hitcount"
        }
        $initTaskResult = Invoke-FMAPI @apiCallParameter
        $taskID = $initTaskResult.result[0].taskid
        Write-PSFMessage "taskID=$taskID"
        $taskStatus = Get-FMTaskStatus -Id $taskID -Wait -Verbose
        Write-PSFMessage "Status of Task $($taskID): $($taskStatus|ConvertTo-Json -Depth 3)"
    }
    else {
        $taskID = $packageInfo."package settings"."hitc-taskid"
        (Get-Date 01.01.1970) + ([System.TimeSpan]::fromseconds(1655983275))
        $timestamp = $packageInfo."package settings"."hitc-timestamp" | Convert-FMTimestampToDate
        Write-PSFMessage -Level Host "Query existing hitcounts from $timestamp"
    }
    $result = Get-FMTaskResult -Id $taskID -verbose
    return $result
}


function Get-FMFirewallPolicy {
    <#
    .SYNOPSIS
    Queries existing IPv4/IPv6 policies.
 
    .DESCRIPTION
    Queries existing IPv4/IPv6 policies.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER EnableException
    If set to True, errors will throw an exception
 
    .PARAMETER Package
    The name of the policy package
 
    .PARAMETER Attr
    Parameter The name of the attribute to retrieve its datasource. Only used
    with datasrc option.
 
    .PARAMETER Fields
    Limit the output by returning only the attributes specified in the string
    array. If none specified, all attributes will be returned.
 
    .PARAMETER Filter
    Filter the result according to a set of criteria.For detailed help see
    about_FortigateManagerFilter
 
    .PARAMETER GetUsed
    Parameter description
 
    .PARAMETER Loadsub
    Enable or disable the return of any sub-objects. If not specified, the
    default is to return all sub-objects.
 
    .PARAMETER Option
    Set fetch option for the request. If no option is specified, by default the
    attributes of the objects will be returned. count - Return the number of
    matching entries instead of the actual entry data. scope member - Return a
    list of scope members along with other attributes. datasrc - Return all
    objects that can be referenced by an attribute. Require attr parameter. get
    reserved - Also return reserved objects in the result. syntax - Return the
    attribute syntax of a table or an object, instead of the actual entry data.
    All filter parameters will be ignored.
 
    .PARAMETER Range
    Limit the number of output. For a range of [a, n], the output will contain n
    elements, starting from the ath matching result.
 
    .PARAMETER Sortings
    Specify the sorting of the returned result.
 
    .PARAMETER IncludeHitCount
    Additional Switch; Query the current hitcount statistik and add the
    attributes to the result data as the following attributes:
    _byte
    _first_hit
    _first_session
    _hitcount
    _pkts
    _sesscount
    _first_hit
    _first_session
    _last_hit
    _last_session
    The _hitcount will be set to -1 if no statistics could be found.
 
    .PARAMETER IncludeHitCountForce
    Same as IncludeHitCount but the Data will be refreshed before.
 
    .PARAMETER NullHandler
    Parameter description
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    See
    https://fndn.fortinet.net/index.php?/fortiapi/5-fortimanager/1636/5/pm/config/firewall/
    #>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [bool]$EnableException = $true,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("FortigateManager.FirewallPackage")]
        [string]$Package,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Attr,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("_policy_block", "action", "anti-replay", "application-list", "auth-cert", "auth-path", "auth-redirect-addr", "auto-asic-offload", "av-profile", "block-notification", "captive-portal-exempt", "capture-packet", "cifs-profile", "comments", "custom-log-fields", "decrypted-traffic-mirror", "delay-tcp-npu-session", "diffserv-forward", "diffserv-reverse", "diffservcode-forward", "diffservcode-rev", "disclaimer", "dlp-profile", "dnsfilter-profile", "dsri", "dstaddr", "dstaddr-negate", "dstaddr6", "dstintf", "dynamic-shaping", "email-collect", "emailfilter-profile", "fec", "file-filter-profile", "firewall-session-dirty", "fixedport", "fsso-agent-for-ntlm", "fsso-groups", "geoip-anycast", "geoip-match", "global-label", "groups", "gtp-profile", "http-policy-redirect", "icap-profile", "identity-based-route", "inbound", "inspection-mode", "internet-service", "internet-service-custom", "internet-service-custom-group", "internet-service-group", "internet-service-name", "internet-service-negate", "internet-service-src", "internet-service-src-custom", "internet-service-src-custom-group", "internet-service-src-group", "internet-service-src-name", "internet-service-src-negate", "ippool", "ips-sensor", "label", "logtraffic", "logtraffic-start", "match-vip", "match-vip-only", "name", "nat", "nat46", "nat64", "natinbound", "natip", "natoutbound", "np-acceleration", "ntlm", "ntlm-enabled-browsers", "ntlm-guest", "outbound", "passive-wan-health-measurement", "per-ip-shaper", "permit-any-host", "permit-stun-host", "pfcp-profile", "policy-expiry", "policy-expiry-date", "policyid", "poolname", "poolname6", "profile-group", "profile-protocol-options", "profile-type", "radius-mac-auth-bypass", "redirect-url", "replacemsg-override-group", "reputation-direction", "reputation-minimum", "rtp-addr", "rtp-nat", "schedule", "schedule-timeout", "sctp-filter-profile", "send-deny-packet", "service", "service-negate", "session-ttl", "sgt", "sgt-check", "src-vendor-mac", "srcaddr", "srcaddr-negate", "srcaddr6", "srcintf", "ssh-filter-profile", "ssh-policy-redirect", "ssl-ssh-profile", "status", "tcp-mss-receiver", "tcp-mss-sender", "tcp-session-without-syn", "timeout-send-rst", "tos", "tos-mask", "tos-negate", "traffic-shaper", "traffic-shaper-reverse", "users", "utm-status", "uuid", "videofilter-profile", "vlan-cos-fwd", "vlan-cos-rev", "vlan-filter", "voip-profile", "vpntunnel", "waf-profile", "wanopt", "wanopt-detection", "wanopt-passive-opt", "wanopt-peer", "wanopt-profile", "wccp", "webcache", "webcache-https", "webfilter-profile", "webproxy-forward-server", "webproxy-profile", "ztna-ems-tag", "ztna-geo-tag", "ztna-status")]
        [System.String[]]$Fields,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.String[]]$Filter,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$GetUsed = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Loadsub = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("count", "scope member", "datasrc", "get reserved", "syntax")]
        [string]$Option,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Range,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Sortings,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [switch]$IncludeHitCount,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [switch]$IncludeHitCountForce,
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute"
    )
    # 'pkg_path' = "$PkgPath"
    $Parameter = @{
        'attr'     = "$Attr"
        'fields'   = @($Fields)
        'filter'   = ($Filter | ConvertTo-FMFilterArray)
        'get used' = $GetUsed
        'loadsub'  = $Loadsub
        'option'   = "$Option"
        'range'    = @($Range)
        'sortings' = @($Sortings)
    } | Remove-FMNullValuesFromHashtable -NullHandler $NullHandler
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM -EnableException $EnableException
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Get-FMFirewallPolicy"
        LoggingActionValues = @($explicitADOM, $Package)
        method              = "get"
        Parameter           = $Parameter
        Path                = "/pm/config/adom/$explicitADOM/pkg/$Package/firewall/policy"
    }
    $result = Invoke-FMAPI @apiCallParameter
    Write-PSFMessage "Result-Status: $($result.result.status)"
    $policyData = $result.result.data
    if ($IncludeHitCount -or $IncludeHitCountForce) {
        $addAttributes = @('byte', 'first_hit', 'first_session', 'hitcount', 'pkts', 'sesscount')
        $addAttributesAsDate = @('first_hit', 'first_session', 'last_hit', 'last_session')
        Write-PSFMessage "Query hitcount statistics, statistics will be added as attributes: _byte, _first_hit, _first_session, _hitcount, _pkts, _sesscount, _first_hit, _first_session, _last_hit, _last_session"
        if($IncludeHitCountForce){
            $hitCountData = Get-FMFirewallHitCount -Package $packageName -Force
        }else{
            $hitCountData = Get-FMFirewallHitCount -Package $packageName
        }
        Write-PSFMessage "Associating hitcount data to policy rules"
        $hitCountDataHash = @{}
        $hitCountData.'firewall policy' | ForEach-Object { $hitCountDataHash.add("$($_.policyid)", $_) }
        $emptyCounter = @{
            byte          = -1
            hitcount      = -1
            pkts          = -1
            sesscount     = -1
            first_hit     = 0
            first_session = 0
            last_hit      = 0
            last_session  = 0
        }
        foreach ($policy in $policyData) {
            $counter = $hitCountDataHash."$($policy.policyid)"
            if ($null -eq $counter) {
                $counter = $emptyCounter
            }
            foreach ($attr in $addAttributes) {
                Add-Member -inputobject $policy -MemberType NoteProperty -Name "_$attr" -Value $counter.$attr -Force
            }
            foreach ($attr in $addAttributesAsDate) {
                Add-Member -inputobject $policy -MemberType NoteProperty -Name "_$attr" -Value (Convert-FMTimestampToDate $counter.$attr).toString() -Force
            }
        }
    }
    Write-PSFMessage "Associating hitcount data to policy rules - done"
    return $policyData
}


function Get-FMFirewallScope {
    <#
    .SYNOPSIS
    Queries firewall scopes for dynamic mapping etc.
 
    .DESCRIPTION
    Queries firewall scopes for dynamic mapping etc.
    The function returns arrays of matched scopes. Will be most usefull if each VDOM is pinned to a
    specific device. Then the VDOM name can be used to identify te full scope.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER EnableException
    If set to True, errors will throw an exception
 
    .PARAMETER VDOM
    The List of VDOMs which should be matched.
 
    .PARAMETER DeviceName
    The List of device names which should be matched.
 
    .EXAMPLE
    Get-FMFirewallScope -vdom "bonn"
 
    Returns @(@{"name"="FW3";"vdom"="bonn"})
 
    .EXAMPLE
    Get-FMFirewallScope -deviceName "FW1"
 
    Returns @(@{"name"="FW1";"vdom"="cologne"},@{"name"="FW1";"vdom"="finance"})
 
    .NOTES
    The data is cached within the connection object. If a refresh is needed you have to create a fresh connection.
 
    Examples are based on the assumption that "Get-FMDeviceInfo" returns (shortened)
    {"object member":[{"name":"FW1","vdom":"cologne"},{"name":"FW1","vdom":"finance"},{"name":"FW2","vdom":"munich"},{"name":"FW3","vdom":"bonn"},{"name":"FW3","vdom":"finance"},{"name":"All_FortiGate"}]}
    #>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [bool]$EnableException = $true,

        [parameter(mandatory = $false, ParameterSetName = "default")]
        [String[]]$VDOM,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [String[]]$DeviceName
    )
    if ($Connection.forti.containsKey('availableScopes')) {
        $availableScopes = $Connection.forti.availableScopes
    }
    else {
        Write-PSFMessage -Level Host "Query Device-Info once from manager, store the scopes unter '`$Connection.forti.availableScopes'"
        $devInfo = Get-FMDeviceInfo -Connection $connection -Option 'object member' -ADOM $ADOM -EnableException $EnableException
        $availableScopes = $devInfo."object member" | Where-Object { $_.vdom } | Select-Object name, vdom
        $Connection.forti.availableScopes = $availableScopes
    }


    $queryFilter = "$($null -eq $DeviceName)|$($null -eq $VDOM)"
    # Write-PSFMessage "Queryfilter=$queryFilter"
    switch ($queryFilter) {
        default { $result = $availableScopes }
        "False|False" { $result = $availableScopes | Where-Object { $_.name -in $DeviceName -and $_.VDOM -in $VDOM } }
        "False|True" { $result = $availableScopes | Where-Object { $_.name -in $DeviceName } }
        "True|False" { $result = $availableScopes | Where-Object { $_.VDOM -in $VDOM } }
    }
    # Write-PSFMessage "`$result = $($result |ConvertTo-Json)"
    return $result
}


function Get-FMFirewallService {
    <#
    .SYNOPSIS
    Queries the firewall service table.
 
    .DESCRIPTION
    Queries the firewall service table.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER EnableException
    If set to True, errors will throw an exception
 
 
    .PARAMETER Attr
    The name of the attribute to retrieve its datasource.
 
    .PARAMETER Fields
    Limit the output by returning only the attributes specified in the string array. If none specified, all attributes will be returned.
 
    .PARAMETER Filter
    Filter the result according to a set of criteria. For detailed help
    see about_FortigateManagerFilter
 
    .PARAMETER GetUsed
    Parameter description
 
    .PARAMETER Loadsub
    Enable or disable the return of any sub-objects. If not specified, the default is to return all sub-objects.
 
    .PARAMETER Option
    Set fetch option for the request. If no option is specified, by default the attributes of the objects will be returned.
    count - Return the number of matching entries instead of the actual entry data.
    scope member - Return a list of scope members along with other attributes.
    datasrc - Return all objects that can be referenced by an attribute. Require attr parameter.
    get reserved - Also return reserved objects in the result.
    syntax - Return the attribute syntax of a table or an object, instead of the actual entry data. All filter parameters will be ignored.
 
    .PARAMETER Range
    Limit the number of output. For a range of [a, n], the output will contain n elements, starting from the ath matching result.
 
    .PARAMETER Sortings
    Specify the sorting of the returned result.
 
    .PARAMETER NullHandler
    Parameter description
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    https://fndn.fortinet.net/index.php?/fortiapi/5-fortimanager/1637/5/pm/config/firewall/
 
    #>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [bool]$EnableException = $true,

        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Attr,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("app-category", "app-service-type", "application", "category", "check-reset-range", "color", "comment", "fabric-object", "fqdn", "helper", "icmpcode", "icmptype", "iprange", "name", "protocol", "protocol-number", "proxy", "sctp-portrange", "session-ttl", "tcp-halfclose-timer", "tcp-halfopen-timer", "tcp-portrange", "tcp-rst-timer", "tcp-timewait-timer", "udp-idle-timer", "udp-portrange", "visibility")]
        [String[]]$Fields,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string[]]$Filter,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$GetUsed = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Loadsub = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("count", "scope member", "datasrc", "get reserved", "syntax")]
        [string]$Option,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Range,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Sortings
    )
    $Parameter = @{
        'attr'     = "$Attr"
        'fields'   = @($Fields)
        'filter'   = ($Filter | ConvertTo-FMFilterArray)
        'get used' = $GetUsed
        'loadsub'  = $Loadsub
        'option'   = "$Option"
        'range'    = @($Range)
        'sortings' = @($Sortings)
    } | Remove-FMNullValuesFromHashtable -NullHandler RemoveAttribute
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM -EnableException $EnableException
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Get-FMFirewallService"
        LoggingActionValues = $explicitADOM
        method              = "get"
        Parameter           = $Parameter
    Path                = "/pm/config/adom/$explicitADOM/obj/firewall/service/custom"
    }

    $result = Invoke-FMAPI @apiCallParameter
    Write-PSFMessage "Result-Status: $($result.result.status)"
    return $result.result.data
}


function Get-FMInterface {
    <#
    .SYNOPSIS
    Querys existing addresses.
 
    .DESCRIPTION
    Querys existing addresses.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER EnableException
    If set to True, errors will throw an exception
 
    .PARAMETER Attr
    The name of the attribute to retrieve its datasource. Only used with datasrc option.
 
    .PARAMETER Fields
    Limit the output by returning only the attributes specified in the string array. If none specified, all attributes will be returned.
 
    .PARAMETER Filter
    Filter the result according to a set of criteria. For detailed help
    see about_FortigateManagerFilter
 
    .PARAMETER GetUsed
    Parameter description
 
    .PARAMETER Loadsub
    Enable or disable the return of any sub-objects. If not specified, the default is to return all sub-objects.
 
    .PARAMETER Option
    Set fetch option for the request. If no option is specified, by default the attributes of the objects will be returned.
    count - Return the number of matching entries instead of the actual entry data.
    scope member - Return a list of scope members along with other attributes.
    datasrc - Return all objects that can be referenced by an attribute. Require attr parameter.
    get reserved - Also return reserved objects in the result.
    syntax - Return the attribute syntax of a table or an object, instead of the actual entry data. All filter parameters will be ignored.
 
 
    .PARAMETER Range
    Limit the number of output. For a range of [a, n], the output will contain n elements, starting from the ath matching result.
 
    .PARAMETER Sortings
    Specify the sorting of the returned result.
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [bool]$EnableException = $true,

        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Attr,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("color", "default-mapping", "defmap-intf", "defmap-intrazone-deny", "defmap-zonemember", "description", "egress-shaping-profile", "ingress-shaping-profile", "name", "single-intf", "wildcard", "wildcard-intf", "zone-only")]
        [System.Object[]]$Fields,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Filter,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$GetUsed = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Loadsub = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("count", "scope member", "datasrc", "get reserved", "syntax")]
        [string]$Option,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Range,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Sortings
    )
    $Parameter = @{
        'attr'     = "$Attr"
        'fields'   = @($Fields)
        'filter'   = ($Filter | ConvertTo-FMFilterArray)
        'get used' = $GetUsed
        'loadsub'  = $Loadsub
        'option'   = "$Option"
        'range'    = @($Range)
        'sortings' = @($Sortings)
    } | Remove-FMNullValuesFromHashtable
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM -EnableException $EnableException
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Get-FMInterface"
        LoggingActionValues = @($explicitADOM)
        method              = "get"
        Parameter           = $Parameter
        Path                = "/pm/config/adom/$explicitADOM/obj/dynamic/interface"
    }

    $result = Invoke-FMAPI @apiCallParameter
    Write-PSFMessage "Result-Status: $($result.result.status)"
    return $result.result.data
}


function Get-FMLastConnection {
    <#
    .SYNOPSIS
    Gets the last connection as default for other function parameters
 
    .DESCRIPTION
    Long description
 
    .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [bool]$EnableException = $true
    )
    $connection = Get-PSFConfigValue -FullName 'FortigateManager.LastConnection'
    if ($null -eq $connection -and $EnableException){
        throw "No last connection available"
    }
    return $connection
}

function Get-FMLog {
    <#
    .SYNOPSIS
    Retrieves the log entries from Write-ARAHCallMessage for a specific request id.
 
    .DESCRIPTION
    Retrieves the log entries from Write-ARAHCallMessage for a specific request id.
 
    .PARAMETER Id
    The Id of the API request. This is logged in every Request prefixed with #
 
    .EXAMPLE
    # If the following is displayed/logged:
    # Execution Confirmed: #63:
    Get-FMLog -Id 63
 
    Retrieves the detailed Logging for the specific request as a JSON string.
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '')]
    param (
        [parameter(mandatory = $true, Position = 1)]
        [int]$Id
    )
    return Get-PSFMessage -Tag "APICALL" | Select-Object -ExpandProperty 'Message' | Where-Object { $_ -match "`"id`": $Id" }
}

function Get-FMPolicyPackage {
    <#
    .SYNOPSIS
    Querys existing Policies.
 
    .DESCRIPTION
    Querys existing Policies.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER EnableException
    If set to True, errors will throw an exception
 
    .PARAMETER Name
    The name of the Package
 
    .PARAMETER Fields
    Limit the output by returning only the attributes specified in the string array. If none specified, all attributes will be returned.
 
    .PARAMETER LoggingLevel
    On which level should die diagnostic Messages be logged?
    Defaults to PSFConfig "FortigateManager.Logging.Api"
 
    .PARAMETER NullHandler
    Parameter description
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [parameter(Mandatory=$false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [bool]$EnableException = $true,

        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Name,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("name", "obj ver", "oid", "scope member", "type")]
        [System.Object[]]$Fields,
        [ValidateSet("Critical", "Important", "Output", "Host", "Significant", "VeryVerbose", "Verbose", "SomewhatVerbose", "System", "Debug", "InternalComment", "Warning")]
        [string]$LoggingLevel = (Get-PSFConfigValue -FullName "FortigateManager.Logging.Api" -Fallback "Verbose"),
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute"
    )
    # 'pkg_path' = "$PkgPath"
    $Parameter = @{
        'fields'   = @($Fields)
    } | Remove-FMNullValuesFromHashtable -NullHandler $NullHandler
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM -EnableException $EnableException
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Get-FMPolicyPackage"
        LoggingActionValues = ($Parameter.Keys.Count)
        method              = "get"
        Parameter           = $Parameter
        Path                = "/pm/pkg/adom/$explicitADOM"
        LoggingLevel        = $LoggingLevel
    }
    if ($Name){
            $apiCallParameter.Path+="/$Name"
    }
    $result = Invoke-FMAPI @apiCallParameter -verbose
    Write-PSFMessage "Result-Status: $($result.result.status)"
    return $result.result.data
}


function Get-FMSystemStatus {
    <#
    .SYNOPSIS
    Disconnects from an existing connection
 
    .DESCRIPTION
    Disconnects from an existing connection
 
    .PARAMETER Connection
    The API connection object.
 
    .EXAMPLE
    To be added
 
    in the Future
 
    .NOTES
    General notes
    #>

    param (
        [parameter(Mandatory)]
        $Connection
    )
    $apiCallParameter = @{
        EnableException = $EnableException
        Connection      = $Connection
        LoggingAction       = "Get-FMSystemStatus"
        LoggingActionValues = ""
        method          = "get"
        Path            = "sys/status"
    }

    Invoke-FMAPI @apiCallParameter
}

function Get-FMTaskResult {
    <#
    .SYNOPSIS
    Queries the results from a prior executed task.
 
    .DESCRIPTION
    Queries the results from a prior executed task.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER Id
    The Task-ID
 
    .PARAMETER Wait
    If set then the task status will be queried until finished/failed.
 
    .PARAMETER EnableException
    If set to True, errors will throw an exception
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
 
    #>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [parameter(mandatory = $true)]
        [string]$Id,
        [bool]$EnableException = $true
    )
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Get-FMTaskResult"
        LoggingActionValues = @($Id)
        method              = "exec"
        Parameter           = @{
            data=@{
                taskid = $Id
            }
        }
        Path                = "/sys/task/result"
    }
    $result = Invoke-FMAPI @apiCallParameter
    return $result.result[0].data
}


function Get-FMTaskStatus {
    <#
    .SYNOPSIS
    Queries the current state of a system task.
 
    .DESCRIPTION
    Queries the current state of a system task.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER Id
    The Task-ID
 
    .PARAMETER Wait
    If set then the task status will be queried until finished/failed.
 
    .PARAMETER EnableException
    If set to True, errors will throw an exception
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    #>

    [CmdletBinding()]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [parameter(mandatory = $true)]
        [string]$Id,
        [switch]$Wait,
        [bool]$EnableException = $true
    )
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Get-FMTaskStatus"
        LoggingActionValues = @($Id)
        method              = "get"
        Path                = "/task/task/$Id"
    }

    do {
        $repeat=$false
        # if($wait){$repeat=$true}
        $result = Invoke-FMAPI @apiCallParameter
        if($wait){
            if (($result.result.data.num_done + $result.result.data.num_err) -eq 0) {
                # Still running
                $repeat=$true
            }
            elseif (($result.result.data.num_err) -gt 0) {
                Write-PSFMessage "Task-Error: $($result.result.data |ConvertTo-Json)"
                if($EnableException){
                    throw "Task Error"
                }
                return $result.result
            }
        }
        if ($repeat){
            Write-PSFMessage "Sleeping before repeating"
            Start-Sleep -Seconds 2
        }
    } while ($repeat    )
    Write-PSFMessage "Result-Status: $($result.result.status)"
    return $result.result.data
}


function Invoke-FMAPI {
    <#
    .SYNOPSIS
    Generic API Call to the Fortigate Manager API.
 
    .DESCRIPTION
    Generic API Call to the Fortigate Manager API. This function is a wrapper for the usage of Invoke-WebRequest. It handles some annoying repetitive tasks which occur in most use cases. This includes (list may be uncompleted)
    - Connecting to a server with authentication
    - Parsing API parameter
    - Handling $null parameter
    - Paging for API endpoints which do only provide limited amounts of datasets
 
    .PARAMETER Connection
    Object of Class , stores the authentication Token and the API Base-URL. Can be obtained with Connect-FM.
 
    .PARAMETER Path
    API Path to the REST function
 
    .PARAMETER Body
    Parameter for the API call; The hashtable is Converted to the POST body by using ConvertTo-Json
 
    .PARAMETER Header
    Additional Header Parameter for the API call; currently dropped but needed as a parameter for the *-FMAR* functions
 
    .PARAMETER URLParameter
    Parameter for the API call; Converted to the GET URL parameter set.
    Example:
    {
        id=4
        name=Jon Doe
    }
    will result in "?id=4&name=Jon%20Doe" being added to the URL Path
 
    .PARAMETER Method
    HTTP Method, Get/Post/Delete/Put/...
 
    .PARAMETER ContentType
    HTTP-ContentType, defaults to "application/json;charset=UTF-8"
 
    .PARAMETER Parameter
    The values for the parameter body part of the API request.
 
    .PARAMETER LoggingAction
    compare ~\FortigateManager\en-us\strings.psd1
    The given string with the prefix "APICall." will be used for logging purposes.
 
    .PARAMETER LoggingActionValues
    compare ~\FortigateManager\en-us\strings.psd1
    Array of placeholder values.
 
    .PARAMETER LoggingLevel
    On which level should die diagnostic Messages be logged?
    Defaults to PSFConfig "FortigateManager.Logging.Api"
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
    .PARAMETER EnableException
    If set to true, inner exceptions will be rethrown. Otherwise the an empty result will be returned.
 
    .EXAMPLE
    $result = Invoke-FMAPI -connection $this -path "" -method POST -body @{login = $credentials.UserName; password = $credentials.GetNetworkCredential().Password; language = "1"; authType = "sql" } -hideparameters $true
 
    Login to the service
 
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]

    param (
        [parameter(ParameterSetName = "pathOnly")]
        [parameter(ParameterSetName = "parameterOnly")]
        [parameter(ParameterSetName = "pathAndParameter")]
        $Connection = (Get-FMLastConnection),
        [parameter(mandatory = $true, ParameterSetName = "pathOnly")]
        [parameter(mandatory = $true, ParameterSetName = "pathAndParameter")]
        [string]$Path,
        [parameter(mandatory = $true, ParameterSetName = "pathAndParameter")]
        [parameter(mandatory = $true, ParameterSetName = "parameterOnly")]
        [Hashtable[]]$Parameter,
        [parameter(mandatory = $true)]
        [ValidateSet("get", "set", "add", "update", "delete", "clone", "exec", "move", "clone")]
        $Method,
        [bool]$EnableException = $true,
        [string]$LoggingAction = "Invoke-FMAPI",
        [ValidateSet("Critical", "Important", "Output", "Host", "Significant", "VeryVerbose", "Verbose", "SomewhatVerbose", "System", "Debug", "InternalComment", "Warning")]
        [string]$LoggingLevel = (Get-PSFConfigValue -FullName "FortigateManager.Logging.Api" -Fallback "Verbose"),
        [string[]]$LoggingActionValues = "",
        [string]$RevisionNote
    )
    if (-not $Connection) {
        Write-PSFMessage "Keine Connection als Parameter erhalten, frage die letzte ab"
        $Connection = Get-FMLastConnection -EnableException $EnableException
        if (-not $Connection) {
            Stop-PSFFunction "No last connection available" -EnableException $EnableException -AlwaysWarning
            return
        }
    }
    if ($Method -in @("set", "add", "update", "delete", "move", "clone")){
        Write-PSFMessage "Performing API call with change character, determin revisionNote"
        $RevisionNote=Resolve-FMRevisionNote -RevisionNote $RevisionNote -EnableException $EnableException -Connection $Connection
        if ($null -eq $RevisionNote){
            Stop-PSFFunction -Message "RevisionNote is needed, provide it for the API-Call or while locking the ADOM" -EnableException $EnableException
            return
        }
    }
    $existingSession = $connection.forti.session
    $requestId = $connection.forti.requestId
    $connection.forti.requestId = $connection.forti.requestId + 1

    $apiCallParameter = @{
        EnableException = $true
        Connection      = $Connection
        method          = "Post"
        Path            = "/jsonrpc"
        Body            = @{
            "id"      = $requestId
            "method"  = "$Method"
            "params"  = @(
                # @{
                # # "data" = @($Parameter )
                # "url" = "$Path"
                # }
            )
            "session" = $existingSession
            "verbose" = 1
        }
    }
    if ($Parameter) {
        # $global:hubba = $apiCallParameter
        $Parameter | ForEach-Object {
            $hashTableClone=$_|ConvertTo-PSFHashtable
            if ([string]::IsNullOrEmpty($hashTableClone.url)) { $hashTableClone.url = $Path }
            $apiCallParameter.body.params += $hashTableClone
        }
        # $apiCallParameter.body.params[0]+=$Parameter
    }else{
        # No parameter given, use only the $Path url
        $apiCallParameter.body.params += @{}
    }
    if (-not [string]::IsNullOrEmpty($Path)){
        $apiCallParameter.body.params | ForEach-Object {
            if ([string]::IsNullOrEmpty($_.url)) { $_.url = $Path }
        }
    }
    if ([string]::IsNullOrEmpty($RevisionNote) -eq $false) {
        Write-PSFMessage "Adding RevisionNote '$RevisionNote' to all parameters"
        $apiCallParameter.body.params | ForEach-Object {
            $_."revision note"=$RevisionNote
        }
    }
    # $apiCallParameter.Body.params[0].url=$Path
    # Invoke-PSFProtectedCommand -ActionString "APICall.$LoggingAction" -ActionStringValues $Url -Target $Url -ScriptBlock {
    Invoke-PSFProtectedCommand -ActionString "APICall.$LoggingAction" -ActionStringValues (,$requestId+$LoggingActionValues) -ScriptBlock {
        $result = Invoke-ARAHRequest @apiCallParameter #-PagingHandler 'FM.PagingHandler'
        # if ($null -eq $result) {
        # Stop-PSFFunction -Message "ADOM could not be locked" -EnableException $EnableException -AlwaysWarning
        # return $false
        # }
        # elseif (-not $EnableException) { return $true }

        if ($null -eq $result) {
            Stop-PSFFunction -Message "No Result delivered" -EnableException $true
            return $false
        }
        $statusCode = $result.result.status.code
        if ($statusCode -ne 0) {
            Stop-PSFFunction -Message "API-Error, statusCode: $statusCode, Message $($result.result.status.Message)" -EnableException $EnableException -StepsUpward 3 #-AlwaysWarning
        }
        $connection.forti.lastApiAccessDate=Get-Date
        return $result

        # } -PSCmdlet $PSCmdlet -EnableException $EnableException -Level (Get-PSFConfigValue -FullName "FortigateManager.Logging.Api" -Fallback "Verbose")
    } -PSCmdlet $PSCmdlet  -EnableException $false -Level $LoggingLevel
    if ((Test-PSFFunctionInterrupt) -and $EnableException) {
        Throw "API-Error, statusCode: $statusCode, Message $($result.result.status.Message)" #-EnableException $true -StepsUpward 3 #-AlwaysWarning
    }
}

function Lock-FMAdom {
    <#
    .SYNOPSIS
    Locks the given ADOM.
 
    .DESCRIPTION
    Locks the given ADOM.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER RevisionNote
    The default revision note for all set, add, update, delete, move and clone calls
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
 
    .EXAMPLE
    To be added
 
    in the Future
 
    .NOTES
    General notes
    #>

    param (
        [parameter(Mandatory=$false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(Mandatory=$true)]
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    # $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
    Write-PSFMessage "Adding defaultRevisionNote to Connection"
    $Connection.forti.defaultRevisionNote = $RevisionNote
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
    # Write-PSFMessage "`$explicitADOM=$explicitADOM"
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Lock-FMAdom"
        LoggingActionValues = $explicitADOM
        method              = "exec"
        Path                = "/dvmdb/adom/$explicitADOM/workspace/lock"
    }
    $result = Invoke-FMAPI @apiCallParameter

    # If EnableException an exception would have be thrown, otherwise the function returns true for success or false for failure
    if (-not $EnableException){
        return ($null -ne $result)
    }
}

function Merge-FMStringHashMap {
    <#
    .SYNOPSIS
    Helper funtion to replace placeholders in an url with values from a hashtable.
 
    .DESCRIPTION
    Helper funtion to replace placeholders in an url with values from a hashtable.
 
    .PARAMETER String
    The string with placeholders. Placeholders may be enclosed with {...} or þ...þ by default.
 
    .PARAMETER Data
    The hashtable with the data for the placeholders.
 
    .PARAMETER PlaceHolderStart
    If you need to change the placeholder enclosement then this is one of the needed parameters.
 
    .PARAMETER PlaceHolderEnd
    If you need to change the placeholder enclosement then this is one of the needed parameters.
 
    .EXAMPLE
    $dataTable=@{
            device='FIREWALL';
            vdom='myVDOM';
        }
    $url="/pm/config/device/{{device}}/vdom/{{vdom}}/system/zone"
    Merge-FMStringHashMap -String $url -Data $dataTable |Should -be
    $url|Merge-FMStringHashMap -Data $dataTable |Should -be "/pm/config/device/FIREWALL/vdom/myVDOM/system/zone"
 
    Returns both "/pm/config/device/FIREWALL/vdom/myVDOM/system/zone"
 
    .EXAMPLE
    $url="/pm/{keepMe}/device/{device}}/vdom/{vdom}}/system/zone"
    $url|Merge-FMStringHashMap -Data $dataTable
 
    Returns "/pm/{keepMe}/device/FIREWALL/vdom/myVDOM/system/zone"
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    param (
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        [string]$String,
        [hashtable]$Data,
        [string]$PlaceHolderStart='{þ',
        [string]$PlaceHolderEnd='}þ'
    )
    begin{
    }
    process{
        $result=$String
        foreach($key in $data.Keys){
            $result = $result -replace "[$PlaceHolderStart]+$key[$PlaceHolderEnd]+", $data.$key
        }
        $result
    }
    end{
    }
}

function Move-FMFirewallPolicy {
    <#
    .SYNOPSIS
    Adds new firewall policies to the given ADOM and policy package.
 
    .DESCRIPTION
    Adds new firewall policies to the given ADOM and policy package. This
    function adds the policy to the End of the Package. If you need it elsewhere
    you have to use ""
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Package
    The name of the policy package
 
    .PARAMETER PolicyID
    The ID of the policy to be moved
 
    .PARAMETER Position
    Before or After
 
    .PARAMETER Target
    The Policy-ID the policies should be moved before/after
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    Lock-FMAdom -Connection $connection
    try {
        #Get all Policies Which contains "Test"
        $allTestPolicies = Get-FMFirewallPolicy -Package $packageName -Filter "name -like %Test%" -Fields policyid
        # move all but the first one after the first one
        $allTestPolicies | Select-Object -skip 1 | Move-FMFirewallPolicy -Package $packageName -position after -Target ($allTestPolicies | Select-Object -First 1)
        Publish-FMAdomChange -Connection $connection
    }
    catch {
        Write-Host "Error $_"
    }
    finally {
        UnLock-FMAdom -Connection $connection
    }
 
    Reorders the Test policies
    .NOTES
    General notes
    #>

    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true)]
        [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("FortigateManager.FirewallPackage")]
        [string]$Package,
        [parameter(mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Object[]]$PolicyID,
        [parameter(mandatory = $true)]
        $Target,
        [parameter(mandatory = $true)]
        [ValidateSet("before", "after")]
        [string]$Position,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $policyList = @()
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        if ($Target -isnot [long]) {
            $Target = $Target.policyid
        }
    }
    process {
        $PolicyID | ForEach-Object {
            $id = $_
            if ($id -isnot [long]) {
                $id = $id.policyid
            }
            write-psfmessage "Adding To List: $id"
            $policyList += $id
        }
    }
    end {
        $basePath = "/pm/config/adom/$explicitADOM/pkg/$Package/firewall/policy"
        $apiCallParameter = @{
            EnableException     = $EnableException
            RevisionNote        = $RevisionNote
            Connection          = $Connection
            LoggingAction       = "Move-FMFirewallPolicy"
            LoggingActionValues = @(0, $Position, $Target, $explicitADOM, $Package)
            method              = "move"
            Parameter           = @{
                target = "$Target"
                option = $Position
            }
        }
        foreach ($id in $policyList) {
            write-psfmessage "Moving ID $id"
            $apiCallParameter.Path = "$basePath/$id"
            $apiCallParameter.LoggingActionValues[0] = $id
            Write-PSFMessage "`$apiCallParameter.LoggingActionValues=$($apiCallParameter.LoggingActionValues | Join-String ';')"
            $result = Invoke-FMAPI @apiCallParameter
            if (-not $EnableException -and $null -ne $result) {
                return $false
            }
        }
        if (-not $EnableException) {
            return $false
        }
    }
}


function New-FMObjAddress {
    <#
    .SYNOPSIS
    Helper for creating new Address-Objects.
 
    .DESCRIPTION
    Helper for creating new Address-Objects.
    Each parameter corresponds to an address attribute with the exception of
    IpRange. This will be split into the attributes StartIp and EndIp
 
    .PARAMETER ImageBase64
    Parameter description
 
    .PARAMETER AllowRouting
    Parameter description
 
    .PARAMETER AssociatedInterface
    Parameter description
 
    .PARAMETER CacheTtl
    Parameter description
 
    .PARAMETER ClearpassSpt
    Parameter description
 
    .PARAMETER Color
    Parameter description
 
    .PARAMETER Comment
    Parameter description
 
    .PARAMETER Country
    Parameter description
 
    .PARAMETER Dirty
    Parameter description
 
    .PARAMETER DynamicMapping
    Parameter description
 
    .PARAMETER EndIp
    Parameter description
 
    .PARAMETER EpgName
    Parameter description
 
    .PARAMETER FabricObject
    Parameter description
 
    .PARAMETER Filter
    Parameter description
 
    .PARAMETER Fqdn
    Parameter description
 
    .PARAMETER FssoGroup
    Parameter description
 
    .PARAMETER Interface
    Parameter description
 
    .PARAMETER IpRange
    Parameter description
 
    .PARAMETER List
    Parameter description
 
    .PARAMETER Macaddr
    Parameter description
 
    .PARAMETER Name
    Parameter description
 
    .PARAMETER NodeIpOnly
    Parameter description
 
    .PARAMETER ObjId
    Parameter description
 
    .PARAMETER ObjTag
    Parameter description
 
    .PARAMETER ObjType
    Parameter description
 
    .PARAMETER Organization
    Parameter description
 
    .PARAMETER PolicyGroup
    Parameter description
 
    .PARAMETER Sdn
    Parameter description
 
    .PARAMETER SdnAddrType
    Parameter description
 
    .PARAMETER SdnTag
    Parameter description
 
    .PARAMETER StartIp
    Parameter description
 
    .PARAMETER SubType
    Parameter description
 
    .PARAMETER Subnet
    Parameter description
 
    .PARAMETER SubnetName
    Parameter description
 
    .PARAMETER TagDetectionLevel
    Parameter description
 
    .PARAMETER TagType
    Parameter description
 
    .PARAMETER Tagging
    Parameter description
 
    .PARAMETER Tenant
    Parameter description
 
    .PARAMETER Type
    Parameter description
 
    .PARAMETER Uuid
    Parameter description
 
    .PARAMETER Wildcard
    Parameter description
 
    .PARAMETER WildcardFqdn
    Parameter description
 
    .PARAMETER NullHandler
    Parameter description
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessforStateChangingFunctions', '')]
    param (
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ImageBase64,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$AllowRouting,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$AssociatedInterface,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$CacheTtl = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ClearpassSpt,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Color = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Comment,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Country,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Dirty,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$DynamicMapping,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$EndIp,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$EpgName,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$FabricObject,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Filter,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Fqdn,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$FssoGroup,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Interface,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$IpRange,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$List,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Macaddr,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [string]$Name,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$NodeIpOnly,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ObjId,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ObjTag,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ObjType,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Organization,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$PolicyGroup,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Sdn,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$SdnAddrType,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$SdnTag,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$StartIp,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$SubType,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Subnet,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$SubnetName,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$TagDetectionLevel,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$TagType,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Tagging,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Tenant,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [ValidateSet("ipmask", "iprange", "dynamic", "fqdn")]
        [string]$Type,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Uuid,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Wildcard,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$WildcardFqdn,
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute"
    )
    if ($IpRange) {
        $singleIps = ConvertTo-FMStartEndIp -IpRange $IpRange
        $StartIp = $singleIps[0]
        $EndIp = $singleIps[1]
    }
    elseif ($Subnet) { $Subnet = Test-FMSubnetCidr -Subnet $Subnet }
    # if ($Subnet -match '^\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b$'){
    # Write-PSFMessage "Subnet $Subnet is missing the subnet mask"
    # $cidr=""
    # switch -regex ($Subnet){
    # '^\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b$'{$cidr="/32"}
    # '^\b\d{1,3}\.\d{1,3}\.\d{1,3}\.0$'{$cidr="/24"}
    # default{
    # Write-PSFMessage "Cannot guess cidr for Subnet $Subnet"
    # }
    # }
    # $Subnet+=$cidr
    # Write-PSFMessage "New Subnet: $Subnet"
    # }
    $data = @{
        '_image-base64'        = "$ImageBase64"
        'allow-routing'        = "$AllowRouting"
        'associated-interface' = "$AssociatedInterface"
        'cache-ttl'            = $CacheTtl
        'clearpass-spt'        = "$ClearpassSpt"
        'color'                = $Color
        'comment'              = "$Comment"
        'country'              = "$Country"
        'dirty'                = "$Dirty"
        'dynamic_mapping'      = @($DynamicMapping)
        'end-ip'               = "$EndIp"
        'epg-name'             = "$EpgName"
        'fabric-object'        = "$FabricObject"
        'filter'               = "$Filter"
        'fqdn'                 = "$Fqdn"
        'fsso-group'           = @($FssoGroup)
        'interface'            = "$Interface"
        'list'                 = @($List)
        'macaddr'              = @($Macaddr)
        'name'                 = "$Name"
        'node-ip-only'         = "$NodeIpOnly"
        'obj-id'               = "$ObjId"
        'obj-tag'              = "$ObjTag"
        'obj-type'             = "$ObjType"
        'organization'         = "$Organization"
        'policy-group'         = "$PolicyGroup"
        'sdn'                  = "$Sdn"
        'sdn-addr-type'        = "$SdnAddrType"
        'sdn-tag'              = "$SdnTag"
        'start-ip'             = "$StartIp"
        'sub-type'             = "$SubType"
        'subnet'               = "$Subnet"
        'subnet-name'          = "$SubnetName"
        'tag-detection-level'  = "$TagDetectionLevel"
        'tag-type'             = "$TagType"
        'tagging'              = @($Tagging)
        'tenant'               = "$Tenant"
        'type'                 = "$Type"
        'uuid'                 = "$Uuid"
        'wildcard'             = "$Wildcard"
        'wildcard-fqdn'        = "$WildcardFqdn"
    }
    $data = $data | Remove-FMNullValuesFromHashtable -NullHandler $NullHandler
    if ($data.subnet) {
        Write-PSFMessage "Converting ipMask $($data.subnet) to CIDR Notation if neccessary"
        $data.subnet = Convert-FMSubnetMask -Target CIDR -IPMask $data.subnet
        Write-PSFMessage " > ipMask= $($data.subnet)"
    }
    return $data
}


function New-FMObjAddressGroup {
    <#
    .SYNOPSIS
    Helper for creating new Addressgroup-Objects.
 
    .DESCRIPTION
    Helper for creating new Addressgroup-Objects.
    Each parameter corresponds to an addressgroup attribute with the exception of
    IpRange. This will be split into the attributes StartIp and EndIp
 
    .PARAMETER ImageBase64
    Parameter description
 
    .PARAMETER AllowRouting
    Parameter description
 
    .PARAMETER Category
    Parameter description
 
    .PARAMETER Color
    Parameter description
 
    .PARAMETER Comment
    Parameter description
 
    .PARAMETER DynamicMapping
    Parameter description
 
    .PARAMETER Exclude
    Parameter description
 
    .PARAMETER ExcludeMember
    Parameter description
 
    .PARAMETER FabricObject
    Parameter description
 
    .PARAMETER Member
    Parameter description
 
    .PARAMETER Name
    Parameter description
 
    .PARAMETER Tagging
    Parameter description
 
    .PARAMETER Type
    Parameter description
 
    .PARAMETER Uuid
    Parameter description
 
    .PARAMETER NullHandler
    Parameter description
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessforStateChangingFunctions", "")]
    param (
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ImageBase64,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$AllowRouting,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Category,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Color = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Comment,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$DynamicMapping,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Exclude,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string[]]$ExcludeMember,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$FabricObject,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [string[]]$Member,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [string]$Name,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Tagging,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Type,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Uuid,
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute"
    )
    $data = @{
        '_image-base64'   = "$ImageBase64"
        'allow-routing'   = "$AllowRouting"
        'category'        = "$Category"
        'color'           = $Color
        'comment'         = "$Comment"
        'dynamic_mapping' = @($DynamicMapping)
        'exclude'         = "$Exclude"
        'exclude-member'  = @($ExcludeMember)
        'fabric-object'   = "$FabricObject"
        'member'          = @($Member)
        'name'            = "$Name"
        'tagging'         = @($Tagging)
        'type'            = "$Type"
        'uuid'            = "$Uuid"
    }
    return $data | Remove-FMNullValuesFromHashtable -NullHandler $NullHandler
}


function New-FMObjDynamicAddressGroupMapping {
    <#
    .SYNOPSIS
    Creates a new DynamicMapping object with the given attributes.
 
    .DESCRIPTION
    Creates a new DynamicMapping object with the given attributes.
    The result can be used for DynamicMapping parameters
 
    .PARAMETER ImageBase64
    Parameter description
 
    .PARAMETER Scope
    Parameter description
 
    .PARAMETER AllowRouting
    Parameter description
 
    .PARAMETER Category
    Parameter description
 
    .PARAMETER Color
    Parameter description
 
    .PARAMETER Comment
    Parameter description
 
    .PARAMETER Exclude
    Parameter description
 
    .PARAMETER ExcludeMember
    Parameter description
 
    .PARAMETER FabricObject
    Parameter description
 
    .PARAMETER GlobalObject
    Parameter description
 
    .PARAMETER Member
    Parameter description
 
    .PARAMETER Tags
    Parameter description
 
    .PARAMETER Type
    Parameter description
 
    .PARAMETER Uuid
    Parameter description
 
    .PARAMETER Visibility
    Parameter description
 
    .PARAMETER NullHandler
    Parameter description
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessforStateChangingFunctions", "")]
    param (
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ImageBase64,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [System.Object[]]$Scope,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$AllowRouting,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Category,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Color = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Comment,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Exclude,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string[]]$ExcludeMember,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$FabricObject,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$GlobalObject = -1,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [string[]]$Member,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string[]]$Tags,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Type,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Uuid,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Visibility,
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute"
    )
    $data = @{
        '_image-base64'  = "$ImageBase64"
        '_scope'         = @($Scope)
        'allow-routing'  = "$AllowRouting"
        'category'       = "$Category"
        'color'          = $Color
        'comment'        = "$Comment"
        'exclude'        = "$Exclude"
        'exclude-member' = @($ExcludeMember)
        'fabric-object'  = "$FabricObject"
        'global-object'  = $GlobalObject
        'member'         = @($Member)
        'tags'           = @($Tags)
        'type'           = "$Type"
        'uuid'           = "$Uuid"
        'visibility'     = "$Visibility"

    }
    return $data | Remove-FMNullValuesFromHashtable -NullHandler $NullHandler
}


function New-FMObjDynamicAddressMapping {
    <#
    .SYNOPSIS
    Creates a new DynamicMapping object with the given attributes for Address-Objects.
 
    .DESCRIPTION
    Creates a new DynamicMapping object with the given attributes.
    The result can be used for DynamicMapping parameters
    Each parameter corresponds to an address attribute with the exception of
    IpRange. This will be split into the attributes StartIp and EndIp
 
    .PARAMETER ImageBase64
    Parameter description
 
    .PARAMETER Scope
    Parameter description
 
    .PARAMETER AllowRouting
    Parameter description
 
    .PARAMETER AssociatedInterface
    Parameter description
 
    .PARAMETER CacheTtl
    Parameter description
 
    .PARAMETER ClearpassSpt
    Parameter description
 
    .PARAMETER Color
    Parameter description
 
    .PARAMETER Comment
    Parameter description
 
    .PARAMETER Country
    Parameter description
 
    .PARAMETER Dirty
    Parameter description
 
    .PARAMETER EndIp
    Parameter description
 
    .PARAMETER EndMac
    Parameter description
 
    .PARAMETER EpgName
    Parameter description
 
    .PARAMETER FabricObject
    Parameter description
 
    .PARAMETER Filter
    Parameter description
 
    .PARAMETER Fqdn
    Parameter description
 
    .PARAMETER FssoGroup
    Parameter description
 
    .PARAMETER GlobalObject
    Parameter description
 
    .PARAMETER Interface
    Parameter description
 
    .PARAMETER IpRange
    Parameter description
 
    .PARAMETER Macaddr
    Parameter description
 
    .PARAMETER NodeIpOnly
    Parameter description
 
    .PARAMETER ObjId
    Parameter description
 
    .PARAMETER ObjTag
    Parameter description
 
    .PARAMETER ObjType
    Parameter description
 
    .PARAMETER Organization
    Parameter description
 
    .PARAMETER PatternEnd
    Parameter description
 
    .PARAMETER PatternStart
    Parameter description
 
    .PARAMETER PolicyGroup
    Parameter description
 
    .PARAMETER Sdn
    Parameter description
 
    .PARAMETER SdnAddrType
    Parameter description
 
    .PARAMETER SdnTag
    Parameter description
 
    .PARAMETER StartIp
    Parameter description
 
    .PARAMETER StartMac
    Parameter description
 
    .PARAMETER SubType
    Parameter description
 
    .PARAMETER Subnet
    Parameter description
 
    .PARAMETER SubnetName
    Parameter description
 
    .PARAMETER TagDetectionLevel
    Parameter description
 
    .PARAMETER TagType
    Parameter description
 
    .PARAMETER Tags
    Parameter description
 
    .PARAMETER Tenant
    Parameter description
 
    .PARAMETER Type
    Parameter description
 
    .PARAMETER Url
    Parameter description
 
    .PARAMETER Uuid
    Parameter description
 
    .PARAMETER Visibility
    Parameter description
 
    .PARAMETER Wildcard
    Parameter description
 
    .PARAMETER WildcardFqdn
    Parameter description
 
    .PARAMETER NullHandler
    Parameter description
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessforStateChangingFunctions", "")]
    param (
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ImageBase64,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Scope,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$AllowRouting,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$AssociatedInterface,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$CacheTtl = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ClearpassSpt,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Color = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Comment,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Country,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Dirty,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$EndIp,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$EndMac,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$EpgName,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$FabricObject,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Filter,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Fqdn,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$FssoGroup,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$GlobalObject = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Interface,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$IpRange,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Macaddr,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$NodeIpOnly,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ObjId,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ObjTag,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ObjType,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Organization,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$PatternEnd = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$PatternStart = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$PolicyGroup,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Sdn,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$SdnAddrType,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$SdnTag,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$StartIp,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$StartMac,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$SubType,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Subnet,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$SubnetName,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$TagDetectionLevel,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$TagType,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Tags,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Tenant,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [string]$Type,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Url,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Uuid,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Visibility,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Wildcard,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$WildcardFqdn,
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute"
    )
    if ($IpRange) {
        $singleIps = ConvertTo-FMStartEndIp -IpRange $IpRange
        $StartIp = $singleIps[0]
        $EndIp = $singleIps[1]
    }elseif ($Subnet) { $Subnet = Test-FMSubnetCidr -Subnet $Subnet }

    $data = @{
        '_image-base64'        = "$ImageBase64"
        '_scope'               = @($Scope)
        'allow-routing'        = "$AllowRouting"
        'associated-interface' = "$AssociatedInterface"
        'cache-ttl'            = $CacheTtl
        'clearpass-spt'        = "$ClearpassSpt"
        'color'                = $Color
        'comment'              = "$Comment"
        'country'              = "$Country"
        'dirty'                = "$Dirty"
        'end-ip'               = "$EndIp"
        'end-mac'              = "$EndMac"
        'epg-name'             = "$EpgName"
        'fabric-object'        = "$FabricObject"
        'filter'               = "$Filter"
        'fqdn'                 = "$Fqdn"
        'fsso-group'           = @($FssoGroup)
        'global-object'        = $GlobalObject
        'interface'            = "$Interface"
        'macaddr'              = @($Macaddr)
        'node-ip-only'         = "$NodeIpOnly"
        'obj-id'               = "$ObjId"
        'obj-tag'              = "$ObjTag"
        'obj-type'             = "$ObjType"
        'organization'         = "$Organization"
        'pattern-end'          = $PatternEnd
        'pattern-start'        = $PatternStart
        'policy-group'         = "$PolicyGroup"
        'sdn'                  = "$Sdn"
        'sdn-addr-type'        = "$SdnAddrType"
        'sdn-tag'              = "$SdnTag"
        'start-ip'             = "$StartIp"
        'start-mac'            = "$StartMac"
        'sub-type'             = "$SubType"
        'subnet'               = "$Subnet"
        'subnet-name'          = "$SubnetName"
        'tag-detection-level'  = "$TagDetectionLevel"
        'tag-type'             = "$TagType"
        'tags'                 = @($Tags)
        'tenant'               = "$Tenant"
        'type'                 = "$Type"
        'url'                  = "$Url"
        'uuid'                 = "$Uuid"
        'visibility'           = "$Visibility"
        'wildcard'             = "$Wildcard"
        'wildcard-fqdn'        = "$WildcardFqdn"

    }
    return $data | Remove-FMNullValuesFromHashtable -NullHandler $NullHandler
}


function New-FMObjFirewallPolicy {
    <#
    .SYNOPSIS
    Creates a new Firewall Policy object with the given attributes.
 
    .DESCRIPTION
    Creates a new Firewall Policy object with the given attributes.
 
    .PARAMETER Policy_block
    Parameter description
 
    .PARAMETER Action
    Parameter description
 
    .PARAMETER AntiReplay
    Parameter description
 
    .PARAMETER ApplicationList
    Parameter description
 
    .PARAMETER AuthCert
    Parameter description
 
    .PARAMETER AuthPath
    Parameter description
 
    .PARAMETER AuthRedirectAddr
    Parameter description
 
    .PARAMETER AutoAsicOffload
    Parameter description
 
    .PARAMETER AvProfile
    Parameter description
 
    .PARAMETER BlockNotification
    Parameter description
 
    .PARAMETER CaptivePortalExempt
    Parameter description
 
    .PARAMETER CapturePacket
    Parameter description
 
    .PARAMETER CifsProfile
    Parameter description
 
    .PARAMETER Comments
    Parameter description
 
    .PARAMETER CustomLogFields
    Parameter description
 
    .PARAMETER DecryptedTrafficMirror
    Parameter description
 
    .PARAMETER DelayTcpNpuSession
    Parameter description
 
    .PARAMETER DiffservForward
    Parameter description
 
    .PARAMETER DiffservReverse
    Parameter description
 
    .PARAMETER DiffservcodeForward
    Parameter description
 
    .PARAMETER DiffservcodeRev
    Parameter description
 
    .PARAMETER Disclaimer
    Parameter description
 
    .PARAMETER DlpProfile
    Parameter description
 
    .PARAMETER DnsfilterProfile
    Parameter description
 
    .PARAMETER Dsri
    Parameter description
 
    .PARAMETER Dstaddr
    Parameter description
 
    .PARAMETER DstaddrNegate
    Parameter description
 
    .PARAMETER Dstaddr6
    Parameter description
 
    .PARAMETER Dstintf
    Parameter description
 
    .PARAMETER DynamicShaping
    Parameter description
 
    .PARAMETER EmailCollect
    Parameter description
 
    .PARAMETER EmailfilterProfile
    Parameter description
 
    .PARAMETER Fec
    Parameter description
 
    .PARAMETER FileFilterProfile
    Parameter description
 
    .PARAMETER FirewallSessionDirty
    Parameter description
 
    .PARAMETER Fixedport
    Parameter description
 
    .PARAMETER FssoAgentForNtlm
    Parameter description
 
    .PARAMETER FssoGroups
    Parameter description
 
    .PARAMETER GeoipAnycast
    Parameter description
 
    .PARAMETER GeoipMatch
    Parameter description
 
    .PARAMETER GlobalLabel
    Parameter description
 
    .PARAMETER Groups
    Parameter description
 
    .PARAMETER GtpProfile
    Parameter description
 
    .PARAMETER HttpPolicyRedirect
    Parameter description
 
    .PARAMETER IcapProfile
    Parameter description
 
    .PARAMETER IdentityBasedRoute
    Parameter description
 
    .PARAMETER Inbound
    Parameter description
 
    .PARAMETER InspectionMode
    Parameter description
 
    .PARAMETER InternetService
    Parameter description
 
    .PARAMETER InternetServiceCustom
    Parameter description
 
    .PARAMETER InternetServiceCustomGroup
    Parameter description
 
    .PARAMETER InternetServiceGroup
    Parameter description
 
    .PARAMETER InternetServiceName
    Parameter description
 
    .PARAMETER InternetServiceNegate
    Parameter description
 
    .PARAMETER InternetServiceSrc
    Parameter description
 
    .PARAMETER InternetServiceSrcCustom
    Parameter description
 
    .PARAMETER InternetServiceSrcCustomGroup
    Parameter description
 
    .PARAMETER InternetServiceSrcGroup
    Parameter description
 
    .PARAMETER InternetServiceSrcName
    Parameter description
 
    .PARAMETER InternetServiceSrcNegate
    Parameter description
 
    .PARAMETER Ippool
    Parameter description
 
    .PARAMETER IpsSensor
    Parameter description
 
    .PARAMETER Label
    Parameter description
 
    .PARAMETER Logtraffic
    Parameter description
 
    .PARAMETER LogtrafficStart
    Parameter description
 
    .PARAMETER MatchVip
    Parameter description
 
    .PARAMETER MatchVipOnly
    Parameter description
 
    .PARAMETER Name
    Parameter description
 
    .PARAMETER Nat
    Parameter description
 
    .PARAMETER Nat46
    Parameter description
 
    .PARAMETER Nat64
    Parameter description
 
    .PARAMETER Natinbound
    Parameter description
 
    .PARAMETER Natip
    Parameter description
 
    .PARAMETER Natoutbound
    Parameter description
 
    .PARAMETER NpAcceleration
    Parameter description
 
    .PARAMETER Ntlm
    Parameter description
 
    .PARAMETER NtlmEnabledBrowsers
    Parameter description
 
    .PARAMETER NtlmGuest
    Parameter description
 
    .PARAMETER Outbound
    Parameter description
 
    .PARAMETER PassiveWanHealthMeasurement
    Parameter description
 
    .PARAMETER PerIpShaper
    Parameter description
 
    .PARAMETER PermitAnyHost
    Parameter description
 
    .PARAMETER PermitStunHost
    Parameter description
 
    .PARAMETER PfcpProfile
    Parameter description
 
    .PARAMETER PolicyExpiry
    Parameter description
 
    .PARAMETER PolicyExpiryDate
    Parameter description
 
    .PARAMETER Policyid
    Parameter description
 
    .PARAMETER Poolname
    Parameter description
 
    .PARAMETER Poolname6
    Parameter description
 
    .PARAMETER ProfileGroup
    Parameter description
 
    .PARAMETER ProfileProtocolOptions
    Parameter description
 
    .PARAMETER ProfileType
    Parameter description
 
    .PARAMETER RadiusMacAuthBypass
    Parameter description
 
    .PARAMETER RedirectUrl
    Parameter description
 
    .PARAMETER ReplacemsgOverrideGroup
    Parameter description
 
    .PARAMETER ReputationDirection
    Parameter description
 
    .PARAMETER ReputationMinimum
    Parameter description
 
    .PARAMETER RtpAddr
    Parameter description
 
    .PARAMETER RtpNat
    Parameter description
 
    .PARAMETER Schedule
    Parameter description
 
    .PARAMETER ScheduleTimeout
    Parameter description
 
    .PARAMETER ScopeMember
    The "Install on" property of a firewall policy; undocumented within the API.
 
    .PARAMETER SctpFilterProfile
    Parameter description
 
    .PARAMETER SendDenyPacket
    Parameter description
 
    .PARAMETER Service
    Parameter description
 
    .PARAMETER ServiceNegate
    Parameter description
 
    .PARAMETER SessionTtl
    Parameter description
 
    .PARAMETER Sgt
    Parameter description
 
    .PARAMETER SgtCheck
    Parameter description
 
    .PARAMETER SrcVendorMac
    Parameter description
 
    .PARAMETER Srcaddr
    Parameter description
 
    .PARAMETER SrcaddrNegate
    Parameter description
 
    .PARAMETER Srcaddr6
    Parameter description
 
    .PARAMETER Srcintf
    Parameter description
 
    .PARAMETER SshFilterProfile
    Parameter description
 
    .PARAMETER SshPolicyRedirect
    Parameter description
 
    .PARAMETER SslSshProfile
    Parameter description
 
    .PARAMETER Status
    Parameter description
 
    .PARAMETER TcpMssReceiver
    Parameter description
 
    .PARAMETER TcpMssSender
    Parameter description
 
    .PARAMETER TcpSessionWithoutSyn
    Parameter description
 
    .PARAMETER TimeoutSendRst
    Parameter description
 
    .PARAMETER Tos
    Parameter description
 
    .PARAMETER TosMask
    Parameter description
 
    .PARAMETER TosNegate
    Parameter description
 
    .PARAMETER TrafficShaper
    Parameter description
 
    .PARAMETER TrafficShaperReverse
    Parameter description
 
    .PARAMETER Users
    Parameter description
 
    .PARAMETER UtmStatus
    Parameter description
 
    .PARAMETER Uuid
    Parameter description
 
    .PARAMETER VideofilterProfile
    Parameter description
 
    .PARAMETER VlanCosFwd
    Parameter description
 
    .PARAMETER VlanCosRev
    Parameter description
 
    .PARAMETER VlanFilter
    Parameter description
 
    .PARAMETER VoipProfile
    Parameter description
 
    .PARAMETER Vpntunnel
    Parameter description
 
    .PARAMETER WafProfile
    Parameter description
 
    .PARAMETER Wanopt
    Parameter description
 
    .PARAMETER WanoptDetection
    Parameter description
 
    .PARAMETER WanoptPassiveOpt
    Parameter description
 
    .PARAMETER WanoptPeer
    Parameter description
 
    .PARAMETER WanoptProfile
    Parameter description
 
    .PARAMETER Wccp
    Parameter description
 
    .PARAMETER Webcache
    Parameter description
 
    .PARAMETER WebcacheHttps
    Parameter description
 
    .PARAMETER WebfilterProfile
    Parameter description
 
    .PARAMETER WebproxyForwardServer
    Parameter description
 
    .PARAMETER WebproxyProfile
    Parameter description
 
    .PARAMETER ZtnaEmsTag
    Parameter description
 
    .PARAMETER ZtnaGeoTag
    Parameter description
 
    .PARAMETER ZtnaStatus
    Parameter description
 
    .PARAMETER NullHandler
    Parameter description
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessforStateChangingFunctions", "")]
    param (
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Policy_block = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("deny", "accept", "ipsec", "ssl-vpn", "redirect", "isolate")]
        [string]$Action,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$AntiReplay,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ApplicationList,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$AuthCert,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$AuthPath,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$AuthRedirectAddr,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$AutoAsicOffload,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$AvProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$BlockNotification,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$CaptivePortalExempt,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$CapturePacket,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$CifsProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Comments,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$CustomLogFields,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$DecryptedTrafficMirror,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$DelayTcpNpuSession,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$DiffservForward,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$DiffservReverse,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$DiffservcodeForward,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$DiffservcodeRev,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Disclaimer,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$DlpProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$DnsfilterProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Dsri,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Dstaddr,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$DstaddrNegate,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Dstaddr6,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Dstintf,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$DynamicShaping,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$EmailCollect,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$EmailfilterProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Fec,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$FileFilterProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$FirewallSessionDirty,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Fixedport,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$FssoAgentForNtlm,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$FssoGroups,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$GeoipAnycast,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$GeoipMatch,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$GlobalLabel,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Groups,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$GtpProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$HttpPolicyRedirect,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$IcapProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$IdentityBasedRoute,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Inbound,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$InspectionMode,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$InternetService,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$InternetServiceCustom,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$InternetServiceCustomGroup,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$InternetServiceGroup,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$InternetServiceName,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$InternetServiceNegate,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$InternetServiceSrc,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$InternetServiceSrcCustom,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$InternetServiceSrcCustomGroup,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$InternetServiceSrcGroup,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$InternetServiceSrcName,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$InternetServiceSrcNegate,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Ippool,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$IpsSensor,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Label,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Logtraffic,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$LogtrafficStart,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$MatchVip,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$MatchVipOnly,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Name,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Nat,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Nat46,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Nat64,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Natinbound,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Natip,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Natoutbound,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$NpAcceleration,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Ntlm,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$NtlmEnabledBrowsers,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$NtlmGuest,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Outbound,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$PassiveWanHealthMeasurement,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$PerIpShaper,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$PermitAnyHost,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$PermitStunHost,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$PfcpProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$PolicyExpiry,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$PolicyExpiryDate,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Policyid = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Poolname,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Poolname6,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ProfileGroup,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ProfileProtocolOptions,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ProfileType,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$RadiusMacAuthBypass,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$RedirectUrl,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ReplacemsgOverrideGroup,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$ReputationDirection,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$ReputationMinimum = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$RtpAddr,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$RtpNat,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Schedule="always",
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$ScheduleTimeout,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [object[]]$ScopeMember,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$SctpFilterProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$SendDenyPacket,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Service,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$ServiceNegate,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$SessionTtl = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Sgt,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$SgtCheck,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$SrcVendorMac,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Srcaddr,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$SrcaddrNegate,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Srcaddr6,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Srcintf,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$SshFilterProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$SshPolicyRedirect,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$SslSshProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Status,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$TcpMssReceiver = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$TcpMssSender = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$TcpSessionWithoutSyn,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$TimeoutSendRst,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Tos,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$TosMask,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$TosNegate,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$TrafficShaper,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$TrafficShaperReverse,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Users,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$UtmStatus,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Uuid,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$VideofilterProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$VlanCosFwd = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$VlanCosRev = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$VlanFilter,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$VoipProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Vpntunnel,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$WafProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Wanopt,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$WanoptDetection,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$WanoptPassiveOpt,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$WanoptPeer,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$WanoptProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Wccp,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Webcache,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$WebcacheHttps,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$WebfilterProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$WebproxyForwardServer,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$WebproxyProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$ZtnaEmsTag,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$ZtnaGeoTag,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$ZtnaStatus,
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute"
    )
    $data = @{
        '_policy_block'                     = $Policy_block
        'action'                            = "$Action"
        'anti-replay'                       = "$AntiReplay"
        'application-list'                  = "$ApplicationList"
        'auth-cert'                         = "$AuthCert"
        'auth-path'                         = "$AuthPath"
        'auth-redirect-addr'                = "$AuthRedirectAddr"
        'auto-asic-offload'                 = "$AutoAsicOffload"
        'av-profile'                        = "$AvProfile"
        'block-notification'                = "$BlockNotification"
        'captive-portal-exempt'             = "$CaptivePortalExempt"
        'capture-packet'                    = "$CapturePacket"
        'cifs-profile'                      = "$CifsProfile"
        'comments'                          = "$Comments"
        'custom-log-fields'                 = @($CustomLogFields)
        'decrypted-traffic-mirror'          = "$DecryptedTrafficMirror"
        'delay-tcp-npu-session'             = "$DelayTcpNpuSession"
        'diffserv-forward'                  = "$DiffservForward"
        'diffserv-reverse'                  = "$DiffservReverse"
        'diffservcode-forward'              = "$DiffservcodeForward"
        'diffservcode-rev'                  = "$DiffservcodeRev"
        'disclaimer'                        = "$Disclaimer"
        'dlp-profile'                       = "$DlpProfile"
        'dnsfilter-profile'                 = "$DnsfilterProfile"
        'dsri'                              = "$Dsri"
        'dstaddr'                           = @($Dstaddr)
        'dstaddr-negate'                    = "$DstaddrNegate"
        'dstaddr6'                          = @($Dstaddr6)
        'dstintf'                           = @($Dstintf)
        'dynamic-shaping'                   = "$DynamicShaping"
        'email-collect'                     = "$EmailCollect"
        'emailfilter-profile'               = "$EmailfilterProfile"
        'fec'                               = "$Fec"
        'file-filter-profile'               = "$FileFilterProfile"
        'firewall-session-dirty'            = "$FirewallSessionDirty"
        'fixedport'                         = "$Fixedport"
        'fsso-agent-for-ntlm'               = "$FssoAgentForNtlm"
        'fsso-groups'                       = @($FssoGroups)
        'geoip-anycast'                     = "$GeoipAnycast"
        'geoip-match'                       = "$GeoipMatch"
        'global-label'                      = "$GlobalLabel"
        'groups'                            = @($Groups)
        'gtp-profile'                       = "$GtpProfile"
        'http-policy-redirect'              = "$HttpPolicyRedirect"
        'icap-profile'                      = "$IcapProfile"
        'identity-based-route'              = "$IdentityBasedRoute"
        'inbound'                           = "$Inbound"
        'inspection-mode'                   = "$InspectionMode"
        'internet-service'                  = "$InternetService"
        'internet-service-custom'           = @($InternetServiceCustom)
        'internet-service-custom-group'     = @($InternetServiceCustomGroup)
        'internet-service-group'            = @($InternetServiceGroup)
        'internet-service-name'             = @($InternetServiceName)
        'internet-service-negate'           = "$InternetServiceNegate"
        'internet-service-src'              = "$InternetServiceSrc"
        'internet-service-src-custom'       = @($InternetServiceSrcCustom)
        'internet-service-src-custom-group' = @($InternetServiceSrcCustomGroup)
        'internet-service-src-group'        = @($InternetServiceSrcGroup)
        'internet-service-src-name'         = @($InternetServiceSrcName)
        'internet-service-src-negate'       = "$InternetServiceSrcNegate"
        'ippool'                            = "$Ippool"
        'ips-sensor'                        = "$IpsSensor"
        'label'                             = "$Label"
        'logtraffic'                        = "$Logtraffic"
        'logtraffic-start'                  = "$LogtrafficStart"
        'match-vip'                         = "$MatchVip"
        'match-vip-only'                    = "$MatchVipOnly"
        'name'                              = "$Name"
        'nat'                               = "$Nat"
        'nat46'                             = "$Nat46"
        'nat64'                             = "$Nat64"
        'natinbound'                        = "$Natinbound"
        'natip'                             = "$Natip"
        'natoutbound'                       = "$Natoutbound"
        'np-acceleration'                   = "$NpAcceleration"
        'ntlm'                              = "$Ntlm"
        'ntlm-enabled-browsers'             = @($NtlmEnabledBrowsers)
        'ntlm-guest'                        = "$NtlmGuest"
        'outbound'                          = "$Outbound"
        'passive-wan-health-measurement'    = "$PassiveWanHealthMeasurement"
        'per-ip-shaper'                     = "$PerIpShaper"
        'permit-any-host'                   = "$PermitAnyHost"
        'permit-stun-host'                  = "$PermitStunHost"
        'pfcp-profile'                      = "$PfcpProfile"
        'policy-expiry'                     = "$PolicyExpiry"
        'policy-expiry-date'                = "$PolicyExpiryDate"
        'policyid'                          = $Policyid
        'poolname'                          = @($Poolname)
        'poolname6'                         = @($Poolname6)
        'profile-group'                     = "$ProfileGroup"
        'profile-protocol-options'          = "$ProfileProtocolOptions"
        'profile-type'                      = "$ProfileType"
        'radius-mac-auth-bypass'            = "$RadiusMacAuthBypass"
        'redirect-url'                      = "$RedirectUrl"
        'replacemsg-override-group'         = "$ReplacemsgOverrideGroup"
        'reputation-direction'              = "$ReputationDirection"
        'reputation-minimum'                = $ReputationMinimum
        'rtp-addr'                          = @($RtpAddr)
        'rtp-nat'                           = "$RtpNat"
        'schedule'                          = "$Schedule"
        'schedule-timeout'                  = "$ScheduleTimeout"
        'scope member'                      = @($ScopeMember)
        'sctp-filter-profile'               = "$SctpFilterProfile"
        'send-deny-packet'                  = "$SendDenyPacket"
        'service'                           = @($Service)
        'service-negate'                    = "$ServiceNegate"
        'session-ttl'                       = $SessionTtl
        'sgt'                               = @($Sgt)
        'sgt-check'                         = "$SgtCheck"
        'src-vendor-mac'                    = @($SrcVendorMac)
        'srcaddr'                           = @($Srcaddr)
        'srcaddr-negate'                    = "$SrcaddrNegate"
        'srcaddr6'                          = @($Srcaddr6)
        'srcintf'                           = @($Srcintf)
        'ssh-filter-profile'                = "$SshFilterProfile"
        'ssh-policy-redirect'               = "$SshPolicyRedirect"
        'ssl-ssh-profile'                   = "$SslSshProfile"
        'status'                            = "$Status"
        'tcp-mss-receiver'                  = $TcpMssReceiver
        'tcp-mss-sender'                    = $TcpMssSender
        'tcp-session-without-syn'           = "$TcpSessionWithoutSyn"
        'timeout-send-rst'                  = "$TimeoutSendRst"
        'tos'                               = "$Tos"
        'tos-mask'                          = "$TosMask"
        'tos-negate'                        = "$TosNegate"
        'traffic-shaper'                    = "$TrafficShaper"
        'traffic-shaper-reverse'            = "$TrafficShaperReverse"
        'users'                             = @($Users)
        'utm-status'                        = "$UtmStatus"
        'uuid'                              = "$Uuid"
        'videofilter-profile'               = "$VideofilterProfile"
        'vlan-cos-fwd'                      = $VlanCosFwd
        'vlan-cos-rev'                      = $VlanCosRev
        'vlan-filter'                       = "$VlanFilter"
        'voip-profile'                      = "$VoipProfile"
        'vpntunnel'                         = "$Vpntunnel"
        'waf-profile'                       = "$WafProfile"
        'wanopt'                            = "$Wanopt"
        'wanopt-detection'                  = "$WanoptDetection"
        'wanopt-passive-opt'                = "$WanoptPassiveOpt"
        'wanopt-peer'                       = "$WanoptPeer"
        'wanopt-profile'                    = "$WanoptProfile"
        'wccp'                              = "$Wccp"
        'webcache'                          = "$Webcache"
        'webcache-https'                    = "$WebcacheHttps"
        'webfilter-profile'                 = "$WebfilterProfile"
        'webproxy-forward-server'           = "$WebproxyForwardServer"
        'webproxy-profile'                  = "$WebproxyProfile"
        'ztna-ems-tag'                      = @($ZtnaEmsTag)
        'ztna-geo-tag'                      = @($ZtnaGeoTag)
        'ztna-status'                       = "$ZtnaStatus"

    }
    return $data | Remove-FMNullValuesFromHashtable -NullHandler $NullHandler
}


function New-FMObjFirewallService {
    <#
    .SYNOPSIS
    Short description
 
    .DESCRIPTION
    Long description
 
    .PARAMETER AppCategory
    Parameter description
 
    .PARAMETER AppServiceType
    Parameter description
 
    .PARAMETER Application
    Parameter description
 
    .PARAMETER Category
    Parameter description
 
    .PARAMETER CheckResetRange
    Parameter description
 
    .PARAMETER Color
    Parameter description
 
    .PARAMETER Comment
    Parameter description
 
    .PARAMETER FabricObject
    Parameter description
 
    .PARAMETER Fqdn
    Parameter description
 
    .PARAMETER Helper
    Parameter description
 
    .PARAMETER Icmpcode
    Parameter description
 
    .PARAMETER Icmptype
    Parameter description
 
    .PARAMETER Iprange
    Parameter description
 
    .PARAMETER Name
    Parameter description
 
    .PARAMETER Protocol
    Parameter description
 
    .PARAMETER ProtocolNumber
    Parameter description
 
    .PARAMETER Proxy
    Parameter description
 
    .PARAMETER SctpPortrange
    Parameter description
 
    .PARAMETER SessionTtl
    Parameter description
 
    .PARAMETER TcpHalfcloseTimer
    Parameter description
 
    .PARAMETER TcpHalfopenTimer
    Parameter description
 
    .PARAMETER TcpPortrange
    Comma seperated List of Ports/Port-Ranges; if the source port should be limited use a colon for seperation
    E.g.
    80,443,8443:1-1000
    would allow access to 80&443 from every sourceport, but the destination 8443 is limited.
 
    .PARAMETER TcpRstTimer
    Parameter description
 
    .PARAMETER TcpTimewaitTimer
    Parameter description
 
    .PARAMETER UdpIdleTimer
    Parameter description
 
    .PARAMETER UdpPortrange
    Comma seperated List of Ports/Port-Ranges; if the source port should be limited use a colon for seperation
    E.g.
    80,443,8443:1-1000
    would allow access to 80&443 from every sourceport, but the destination 8443 is limited.
 
 
    .PARAMETER Visibility
    Parameter description
 
    .PARAMETER NullHandler
    Parameter description
 
    .EXAMPLE
    An example
 
    may be provided later
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessforStateChangingFunctions", "")]
    param (
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$AppCategory,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "app-id", "app-category")]
        [string]$AppServiceType,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$Application,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Category,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "default", "strict")]
        [string]$CheckResetRange,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Color = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Comment,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$FabricObject,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Fqdn,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "auto", "ftp", "tftp", "ras", "h323", "tns", "mms", "sip", "pptp", "rtsp", "dns-udp", "dns-tcp", "pmap", "rsh", "dcerpc", "mgcp", "gtp-c", "gtp-u", "gtp-b", "pfcp")]
        [string]$Helper,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Icmpcode = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Icmptype = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Iprange,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [string]$Name,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("ICMP", "IP", "TCP/UDP/SCTP", "ICMP6", "HTTP", "FTP", "CONNECT", "SOCKS", "ALL", "SOCKS-TCP", "SOCKS-UDP")]
        [string]$Protocol,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$ProtocolNumber = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Proxy,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$SctpPortrange,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$SessionTtl = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$TcpHalfcloseTimer = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$TcpHalfopenTimer = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$TcpPortrange,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$TcpRstTimer = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$TcpTimewaitTimer = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$UdpIdleTimer = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$UdpPortrange,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Visibility,
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute"
    )
    if ($Protocol -in @("IP", "TCP/UDP/SCTP") -and [string]::IsNullOrEmpty($Iprange)) {
        $Iprange = '0.0.0.0'
    }
    $data = @{
        'app-category'        = @($AppCategory)
        'app-service-type'    = "$AppServiceType"
        'application'         = @($Application)
        'category'            = "$Category"
        'check-reset-range'   = "$CheckResetRange"
        'color'               = $Color
        'comment'             = "$Comment"
        'fabric-object'       = "$FabricObject"
        'fqdn'                = "$Fqdn"
        'helper'              = "$Helper"
        'icmpcode'            = $Icmpcode
        'icmptype'            = $Icmptype
        'iprange'             = $Iprange
        'name'                = "$Name"
        'protocol'            = "$Protocol"
        'protocol-number'     = $ProtocolNumber
        'proxy'               = "$Proxy"
        'sctp-portrange'      = "$SctpPortrange"
        'session-ttl'         = $SessionTtl
        'tcp-halfclose-timer' = $TcpHalfcloseTimer
        'tcp-halfopen-timer'  = $TcpHalfopenTimer
        'tcp-portrange'       = $TcpPortrange -split ','
        'tcp-rst-timer'       = $TcpRstTimer
        'tcp-timewait-timer'  = $TcpTimewaitTimer
        'udp-idle-timer'      = $UdpIdleTimer
        'udp-portrange'       = $UdpPortrange -split ','
        'visibility'          = "$Visibility"
    }
    return $data | Remove-FMNullValuesFromHashtable -NullHandler $NullHandler
}


function New-FMObjInterface {
    <#
    .SYNOPSIS
    Creates a new Firewall Policy object with the given attributes.
 
    .DESCRIPTION
    Creates a new Firewall Policy object with the given attributes.
 
    .PARAMETER Color
    Parameter description
 
    .PARAMETER DefaultMapping
    Parameter description
 
    .PARAMETER DefmapIntf
    Parameter description
 
    .PARAMETER DefmapIntrazoneDeny
    Parameter description
 
    .PARAMETER DefmapZonemember
    Parameter description
 
    .PARAMETER Description
    Parameter description
 
    .PARAMETER Dynamic_mapping
    Parameter description
 
    .PARAMETER EgressShapingProfile
    Parameter description
 
    .PARAMETER IngressShapingProfile
    Parameter description
 
    .PARAMETER Name
    Parameter description
 
    .PARAMETER Platform_mapping
    Parameter description
 
    .PARAMETER SingleIntf
    Parameter description
 
    .PARAMETER Wildcard
    Parameter description
 
    .PARAMETER WildcardIntf
    Parameter description
 
    .PARAMETER ZoneOnly
    Parameter description
 
    .PARAMETER NullHandler
    Parameter description
 
    .EXAMPLE
    $newInterface = New-FMObjInterface -Name "PESTER"
 
    Creates a a new interface object.
 
    .NOTES
    General notes
    #>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessforStateChangingFunctions', '')]
    param (
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [long]$Color = -1,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$DefaultMapping,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$DefmapIntf,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$DefmapIntrazoneDeny,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$DefmapZonemember,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$Description,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [System.Object[]]$Dynamic_mapping,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$EgressShapingProfile,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [System.Object[]]$IngressShapingProfile,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [string]$Name,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [System.Object[]]$Platform_mapping,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$SingleIntf,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$Wildcard,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [string]$WildcardIntf,
        [parameter(mandatory = $false, ParameterSetName = "default")]
        [ValidateSet("disable", "enable")]
        [string]$ZoneOnly,
        [ValidateSet("Keep", "RemoveAttribute", "ClearContent")]
        [parameter(mandatory = $false, ParameterSetName = "default")]
        $NullHandler = "RemoveAttribute"
    )
    $data = @{
        'color'                   = $Color
        'default-mapping'         = "$DefaultMapping"
        'defmap-intf'             = "$DefmapIntf"
        'defmap-intrazone-deny'   = "$DefmapIntrazoneDeny"
        'defmap-zonemember'       = @($DefmapZonemember)
        'description'             = "$Description"
        'dynamic_mapping'         = @($Dynamic_mapping)
        'egress-shaping-profile'  = @($EgressShapingProfile)
        'ingress-shaping-profile' = @($IngressShapingProfile)
        'name'                    = "$Name"
        'platform_mapping'        = @($Platform_mapping)
        'single-intf'             = "$SingleIntf"
        'wildcard'                = "$Wildcard"
        'wildcard-intf'           = "$WildcardIntf"
        'zone-only'               = "$ZoneOnly"

    }
    return $data | Remove-FMNullValuesFromHashtable -NullHandler $NullHandler
}


function Publish-FMAdomChange {
    <#
    .SYNOPSIS
    Commits changes to the given ADOM.
 
    .DESCRIPTION
    Commits changes to the given ADOM.
 
    .PARAMETER Connection
    The API connection object.
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .EXAMPLE
    To be added
 
    in the Future
 
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param (
        [parameter(Mandatory=$false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [bool]$EnableException = $true
    )
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
    Write-PSFMessage "`$explicitADOM=$explicitADOM"
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Publish-FMAdomChange"
        method              = "exec"
        Path                = "/dvmdb/adom/$explicitADOM/workspace/commit"
    }

    $result = Invoke-FMAPI @apiCallParameter
    if (-not $EnableException) {
        return ($null -ne $result)
    }
}

function Remove-FMAddress {
    <#
    .SYNOPSIS
    Removes a firewall address by name.
 
    .DESCRIPTION
    Removes a firewall address by name.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Name
    Name of the address to be removed.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .PARAMETER Force
    If used addresses can be removed even if it is used within the ADOM.
 
    .EXAMPLE
    $testAddresses = Get-FMAddress -Connection $Connection -Filter "comment -like %API Test%" |select -first 3
    Lock-FMAdom -Connection $connection
    try {
        $testAddresses | remove-fmaddress -connection $connection
        Publish-FMAdomChange -Connection $connection
    }
    catch {
        Write-PSFMessage -Level Warning "$_"
    }
    finally {
        UnLock-FMAdom -Connection $connection
    }
 
    Query addresses by comment and removes them
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [string]$RevisionNote,
        [bool]$EnableException = $true,
        [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string[]]$Name,
        [switch]$Force
    )
    begin {
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        if ($Commit) {
            Publish-FMAdomChange -Connection $Connection -Adom $explicitADOM -EnableException $EnableException
        }
        $apiCallParameter = @{
            EnableException     = $EnableException
            RevisionNote        = $RevisionNote
            Connection          = $Connection
            LoggingActionValues = $explicitADOM
            method              = "delete"
            parameter           = @()
        }
        $addressList = @()
    }
    process {
        $addressList += $Name #-replace '/', '\/'
    }
    end {
        # Removing potential Null values
        $addressList = $addressList | Where-Object { $_ } | ConvertTo-FMUrlPart
        # Different Logging if multiple or single address should be deleted
        if ($addressList.count -gt 1) {
            $apiCallParameter.LoggingAction = "Remove-FMAddress-Multiple"
            $apiCallParameter.LoggingActionValues = $addressList.count
        }
        else {
            $apiCallParameter.LoggingAction = "Remove-FMAddress"
            $apiCallParameter.LoggingActionValues = $addressList
        }
        foreach ($addrName in $addressList) {
            $dataSet = @{
                url = "/pm/config/adom/$explicitADOM/obj/firewall/address/$addrName"
            }
            if ($Force) {
                $dataSet.option = 'force'
            }
            $apiCallParameter.parameter += $dataSet
        }
        $result = Invoke-FMAPI @apiCallParameter
        # If EnableException an exception would have be thrown, otherwise the function returns true for success or false for failure
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }

}

function Remove-FMAddressGroup {
    <#
    .SYNOPSIS
    Removes a firewall address group by name.
 
    .DESCRIPTION
    Removes a firewall address group by name.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Name
    Name of the address group to be removed.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    $testGroup = Get-FMAddressGroup -Connection $Connection -Filter "name -eq Dummy-Group"
    Lock-FMAdom -Connection $connection
    try {
        $testGroup | remove-fmaddressgroup -connection $connection
        Publish-FMAdomChange -Connection $connection
    }
    catch {
        Write-PSFMessage -Level Warning "$_"
    }
    finally {
        UnLock-FMAdom -Connection $connection
    }
    Disconnect-FM -connection $Connection
 
    Removes the named address group.
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [parameter(Mandatory=$false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [string]$RevisionNote,
        [bool]$EnableException = $true,
        [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string[]]$Name
    )
    begin {
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        if ($Commit) {
            Publish-FMAdomChange -Connection $Connection -Adom $explicitADOM -EnableException $EnableException
        }
        $apiCallParameter = @{
            EnableException     = $EnableException
            RevisionNote        = $RevisionNote
            Connection          = $Connection
            LoggingAction       = "Remove-FMAddressGroup"
            LoggingActionValues = $explicitADOM
            method              = "delete"
        }
        $groupList = @()
    }
    process {
        $groupList += $Name #-replace '/', '\/'
    }
    end {
        # Removing potential Null values
        $groupList = $groupList | Where-Object { $_ } | ConvertTo-FMUrlPart
        foreach ($groupName in $groupList) {
            $apiCallParameter.Path = "/pm/config/adom/$explicitADOM/obj/firewall/addrgrp/$groupName"
            $apiCallParameter.LoggingActionValues = $groupName
            $result = Invoke-FMAPI @apiCallParameter
        }
        # If EnableException an exception would have be thrown, otherwise the function returns true for success or false for failure
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }

}

function Remove-FMAdomRevision {
    <#
    .SYNOPSIS
    Removes an existing ADOM revision.
 
    .DESCRIPTION
    Removes an existing ADOM revision.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Version
    Version number of the revision to be removed.
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    Remove-FMAdomRevision -Version 3
 
    Removes the revision with the ID 3
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string[]]$Version,
        [bool]$EnableException = $true
    )
    begin {
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        $apiCallParameter = @{
            EnableException     = $EnableException
            RevisionNote        = $RevisionNote
            Connection          = $Connection
            LoggingActionValues = $explicitADOM
            method              = "delete"
            parameter           = @()
        }
        $revisionList = @()
    }
    process {
        # foreach($id in $version){
        # Write-PSFMessage "$id = $($id.GetType())"
        # $revisionList+=$id
        # }
        $revisionList += $Version
    }
    end {
        # Removing potential Null values
        $revisionList = $revisionList | Where-Object { $_ }
        Write-PSFMessage "Removing revisions $($revisionList -join ',')"
        # Different Logging if multiple or single address should be deleted
        if ($revisionList.count -gt 1) {
            $apiCallParameter.LoggingAction = "Remove-FMAdomRevision-Multiple"
            $apiCallParameter.LoggingActionValues = $revisionList.count
        }
        else {
            $apiCallParameter.LoggingAction = "Remove-FMAdomRevision"
            $apiCallParameter.LoggingActionValues = $revisionList
        }
        foreach ($id in $revisionList) {
            $dataSet = @{
                url = "/dvmdb/adom/$explicitADOM/revision/$id"
            }
            $apiCallParameter.parameter += $dataSet
        }
        $result = Invoke-FMAPI @apiCallParameter
        # If EnableException an exception would have be thrown, otherwise the function returns true for success or false for failure
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }

}

function Remove-FMFirewallPolicy {
    <#
    .SYNOPSIS
    Removes a firewall policy by policyId.
 
    .DESCRIPTION
    Removes a firewall policy by policyId.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Package
    The name of the policy package
 
    .PARAMETER PolicyId
    Id of the Policy to be removed.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    $testAddresses = Get-FMAddress -Connection $Connection -Filter "comment -like %API Test%" |select -first 3
    Lock-FMAdom -Connection $connection
    try {
        $testAddresses | remove-fmaddress -connection $connection
        Publish-FMAdomChange -Connection $connection
    }
    catch {
        Write-PSFMessage -Level Warning "$_"
    }
    finally {
        UnLock-FMAdom -Connection $connection
    }
 
    Query addresses by comment and removes them
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [string]$RevisionNote,
        [bool]$EnableException = $true,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("FortigateManager.FirewallPackage")]
        [string]$Package,
        [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [long[]]$PolicyId
    )
    begin {
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        $apiCallParameter = @{
            EnableException     = $EnableException
            RevisionNote        = $RevisionNote
            Connection          = $Connection
            LoggingAction       = "Remove-FMFirewallPolicy.Multi"
            LoggingActionValues = $explicitADOM
            method              = "delete"
            Path                = "/pm/config/adom/$explicitADOM/pkg/$Package/firewall/policy"
        }
        $policyIdList = @()
    }
    process {
        $policyIdList += $PolicyId #-replace '/', '\/'
    }
    end {
        # Removing potential Null values
        $policyIdList = $policyIdList | Where-Object { $_ }
        $apiCallParameter.Parameter = @{
            confirm = 1
            filter  = @("policyid", "in") + $policyIdList
        }
        if ($policyIdList.count -eq 0) {
            $apiCallParameter.LoggingAction = "Remove-FMFirewallPolicy"
            $apiCallParameter.LoggingActionValues = $policyIdList[0]
        }
        else {
            $apiCallParameter.LoggingActionValues = $policyIdList.count
        }
        # foreach ($id in $policyIdList) {
        # $apiCallParameter.Path = "/pm/config/adom/$explicitADOM/pkg/$Package/firewall/policy/$id"
        # $apiCallParameter.LoggingActionValues = $id
        # $result = Invoke-FMAPI @apiCallParameter
        # $result|Out-Null
        # }
        $result = Invoke-FMAPI @apiCallParameter
        $result | Out-Null
    }
}

function Remove-FMInterface {
    <#
    .SYNOPSIS
    Removes a firewall Interface by name.
 
    .DESCRIPTION
    Removes a firewall Interface by name.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Name
    Name of the Interface to be removed.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    $testInterfacees = Get-FMInterface -Connection $Connection -Filter "comment -like %API Test%" |select -first 3
    Lock-FMAdom -Connection $connection
    try {
        $testInterfacees | remove-fmInterface -connection $connection
        Publish-FMAdomChange -Connection $connection
    }
    catch {
        Write-PSFMessage -Level Warning "$_"
    }
    finally {
        UnLock-FMAdom -Connection $connection
    }
 
    Query Interfacees by comment and removes them
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [parameter(Mandatory=$false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [string]$RevisionNote,
        [bool]$EnableException = $true,
        [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [string[]]$Name
    )
    begin {
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        if ($Commit) {
            Publish-FMAdomChange -Connection $Connection -Adom $explicitADOM -EnableException $EnableException
        }
        $apiCallParameter = @{
            EnableException     = $EnableException
            RevisionNote        = $RevisionNote
            Connection          = $Connection
            LoggingAction       = "Remove-FMInterface"
            LoggingActionValues = $explicitADOM
            method              = "delete"
        }
        $InterfaceList = @()
    }
    process {
        $InterfaceList += $Name #-replace '/', '\/'
    }
    end {
        # Removing potential Null values
        $InterfaceList = $InterfaceList | Where-Object { $_ } | ConvertTo-FMUrlPart
        foreach ($addrName in $InterfaceList) {
            $apiCallParameter.Path = "/pm/config/adom/$explicitADOM/obj/dynamic/interface/$addrName"
            $apiCallParameter.LoggingActionValues = $addrName
            $result = Invoke-FMAPI @apiCallParameter
        }
        # If EnableException an exception would have be thrown, otherwise the function returns true for success or false for failure
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }

}

function Rename-FMAddress {
    <#
    .SYNOPSIS
    Renames an existing addresses.
 
    .DESCRIPTION
    Renames an existing addresses.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Name
    The name of the address to be changed
 
    .PARAMETER NewName
    The new name of the address
 
    .PARAMETER Mapping
    A mapping table between old (=Key) and new (=Value) name.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
    .PARAMETER EnableException
    If set to true, inner exceptions will be rethrown. Otherwise the an empty result will be returned.
 
    .EXAMPLE
    Rename-FMAddress -Name "MyDUMMY" -NewName "MyDummy 2"
 
    Performs the renaming.
 
    .EXAMPLE
    $renameMatrix = @{
                    "PESTER Tick $pesterGUID" = "PESTER Huey $pesterGUID"
                    "PESTER Trick $pesterGUID" = "PESTER Dewey $pesterGUID"
                    "PESTER Track $pesterGUID" = "PESTER Louie $pesterGUID"
                }
    Rename-FMAddress -Mapping $renameMatrix
 
    Performs the renaming of all three addresses.
 
    .NOTES
    General notes
    #>

    param (
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ParameterSetName = "single")]
        [string]$Name,
        [parameter(mandatory = $true, ParameterSetName = "single")]
        [string]$NewName,
        [parameter(mandatory = $true, ParameterSetName = "multiple")]
        [Hashtable]$Mapping,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM

    if ($PSCmdlet.ParameterSetName -eq 'single') {
        $Mapping = @{$name = $NewName }
    }
    $apiCallParameter = @{
        EnableException     = $EnableException
        RevisionNote        = $RevisionNote
        Connection          = $Connection
        LoggingAction       = "Rename-FMAddress"
        LoggingActionValues = @($Mapping.count, $explicitADOM)
        method              = "update"
        Parameter           = @()
    }
    foreach ($oldName in $Mapping.Keys) {
        $apiCallParameter.Parameter += @{
            url  = "/pm/config/adom/$explicitADOM/obj/firewall/address/$($oldName|ConvertTo-FMUrlPart)"
            data = @{name = $Mapping.$oldName }
        }
    }
    # Write-PSFMessage -Level Host "`$apiCallParameter=$($apiCallParameter|ConvertTo-PSFHashtable -Exclude connection |ConvertTo-Json -Depth 4)"
    $result = Invoke-FMAPI @apiCallParameter
    if (-not $EnableException) {
        return $result
        return ($null -ne $result)
    }
}

function Rename-FMAddressGroup {
    <#
    .SYNOPSIS
    Renames an existing address group.
 
    .DESCRIPTION
    Renames an existing address group.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Name
    The name of the address group to be changed
 
    .PARAMETER NewName
    The new name of the address group
 
    .PARAMETER Mapping
    A mapping table between old (=Key) and new (=Value) name.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
    .PARAMETER EnableException
    If set to true, inner exceptions will be rethrown. Otherwise the an empty result will be returned.
 
    .EXAMPLE
    Rename-FMAddressGroup -Name "MyDUMMY" -NewName "MyDummy 2"
 
    Performs the renaming.
 
    .EXAMPLE
    $renameMatrix = @{
                    "PESTER Tick $pesterGUID" = "PESTER Huey $pesterGUID"
                    "PESTER Trick $pesterGUID" = "PESTER Dewey $pesterGUID"
                    "PESTER Track $pesterGUID" = "PESTER Louie $pesterGUID"
                }
    Rename-FMAddressGroup -Mapping $renameMatrix
 
    Performs the renaming of all three addresses. See notes for restrictions.
 
    .NOTES
    If you want to rename multiple groups in one call, take a look at the
    membership of the groups. If e.g.
    Group A contains Group B
    you cannot rename both in one call. Or at least be
    aware that this might happen ;-)
 
    #>

    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ParameterSetName = "single")]
        [string]$Name,
        [parameter(mandatory = $true, ParameterSetName = "single")]
        [string]$NewName,
        [parameter(mandatory = $true, ParameterSetName = "multiple")]
        [Hashtable]$Mapping,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    # return Update-FMAddressGroup -Address @{name = $NewName } -Connection $Connection -ADOM $ADOM -Name $Name -EnableException $EnableException
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM

    if ($PSCmdlet.ParameterSetName -eq 'single') {
        $Mapping = @{$name = $NewName }
    }
    $apiCallParameter = @{
        RevisionNote        = $RevisionNote
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Rename-FMAddressGroup"
        LoggingActionValues = @($Mapping.count, $explicitADOM)
        method              = "update"
        Parameter           = @()
    }
    foreach ($oldName in $Mapping.Keys) {
        $apiCallParameter.Parameter += @{
            url  = "/pm/config/adom/$explicitADOM/obj/firewall/addrgrp/$($oldName|ConvertTo-FMUrlPart)"
            data = @{name = $Mapping.$oldName }
        }
    }
    # Write-PSFMessage -Level Host "`$apiCallParameter=$($apiCallParameter|ConvertTo-PSFHashtable -Exclude connection |ConvertTo-Json -Depth 4)"
    $result = Invoke-FMAPI @apiCallParameter
    if (-not $EnableException) {
        return $result
        return ($null -ne $result)
    }
}

function Rename-FMInterface {
    <#
    .SYNOPSIS
    Renames an existing Interfacees.
 
    .DESCRIPTION
    Renames an existing Interfacees.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Name
    The name of the Interface to be changed
 
    .PARAMETER NewName
    The new name of the Interface
 
    .PARAMETER Mapping
    A mapping table between old (=Key) and new (=Value) name.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
    .PARAMETER EnableException
    If set to true, inner exceptions will be rethrown. Otherwise the an empty result will be returned.
 
    .EXAMPLE
    Rename-FMInterface -Name "MyDUMMY" -NewName "MyDummy 2"
 
    Performs the renaming.
 
    .EXAMPLE
    $renameMatrix = @{
                    "PESTER Tick $pesterGUID" = "PESTER Huey $pesterGUID"
                    "PESTER Trick $pesterGUID" = "PESTER Dewey $pesterGUID"
                    "PESTER Track $pesterGUID" = "PESTER Louie $pesterGUID"
                }
    Rename-FMInterface -Mapping $renameMatrix
 
    Performs the renaming of all three Interfacees.
 
    .NOTES
    General notes
    #>

    param (
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ParameterSetName = "single")]
        [string]$Name,
        [parameter(mandatory = $true, ParameterSetName = "single")]
        [string]$NewName,
        [parameter(mandatory = $true, ParameterSetName = "multiple")]
        [Hashtable]$Mapping,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM

    if ($PSCmdlet.ParameterSetName -eq 'single') {
        $Mapping = @{$name = $NewName }
    }
    $apiCallParameter = @{
        RevisionNote        = $RevisionNote
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Rename-FMInterface"
        LoggingActionValues = @($Mapping.count, $explicitADOM)
        method              = "update"
        Parameter           = @()
    }
    foreach ($oldName in $Mapping.Keys) {
        $apiCallParameter.Parameter += @{
            url  = "/pm/config/adom/$explicitADOM/obj/dynamic/interface/$($oldName|ConvertTo-FMUrlPart)"
            data = @{name = $Mapping.$oldName }
        }
    }
    # Write-PSFMessage -Level Host "`$apiCallParameter=$($apiCallParameter|ConvertTo-PSFHashtable -Exclude connection |ConvertTo-Json -Depth 4)"
    $result = Invoke-FMAPI @apiCallParameter
    if (-not $EnableException) {
        return $result
        return ($null -ne $result)
    }
}

function Unlock-FMAdom {
    <#
    .SYNOPSIS
    Unlocks the given ADOM.
 
    .DESCRIPTION
    Unlocks the given ADOM.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Commit
    If used the changes will be commited before unlocking the ADOM.
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    To be added
 
    in the Future
 
    .NOTES
    General notes
    #>

    param (
        [parameter(Mandatory=$false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [bool]$EnableException = $true,
        [switch]$Commit
    )
    $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
    Write-PSFMessage "`$explicitADOM=$explicitADOM"
    if ($Commit) {
        Publish-FMAdomChange -Connection $Connection -Adom $explicitADOM -EnableException $EnableException
    }
    if (-not [string]::IsNullOrEmpty($Connection.forti.defaultRevisionNote)){
        Write-PSFMessage "Removing defaultRevisionNote from Connection"
        $Connection.forti.remove("defaultRevisionNote")
    }
    $apiCallParameter = @{
        EnableException     = $EnableException
        Connection          = $Connection
        LoggingAction       = "Unlock-FMAdom"
        LoggingActionValues = $explicitADOM
        method              = "exec"
        Path                = "/dvmdb/adom/$explicitADOM/workspace/unlock"
    }

    $result = Invoke-FMAPI @apiCallParameter
    # If EnableException an exception would have be thrown, otherwise the function returns true for success or false for failure
    if (-not $EnableException) {
        return ($null -ne $result)
    }
}

function Update-FMAddress {
    <#
    .SYNOPSIS
    Updates an existing addresses.
 
    .DESCRIPTION
    Updates an existing addresses.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Name
    The name of the address to be changed (neccessary if the 'name' property itself changes)
    This is the *old* Name of the existing object! The new name has to be set in the object itself.
 
    .PARAMETER Address
    The new address, generated e.g. by using New-FMObjAddress or Get-FMAddress
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    $testAddress=Get-FMAddress -filter "name -eq MyDUMMY" -verbose
    $testAddress.comment="Modified at $(Get-Date)"
    $testAddress | Update-fmaddress
 
    Sets the comment field (must exist or you need to have e.g. Add-Member.)
 
    .EXAMPLE
    $testAddress=Get-FMAddress -filter "name -eq MyDUMMY" -verbose
    $testAddress.name="MyDUMMY 2"
    Update-fmaddress -Address $testAddress -Name "MyDUMMY"
 
    Renames the address "MyDUMMY" to "MyDUMMY 2"
 
    .EXAMPLE
    Update-fmaddress -Address @{name="MyDUMMY 2"} -Name "MyDUMMY"
 
    Renames the address "MyDUMMY" to "MyDUMMY 2", everything else will be kept unchanged.
 
    .NOTES
    If an address object does not contain every attribute which is already set,
    then the existing values will be kept.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        [object[]]$Address,
        [string]$Name,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $addressList = @()
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        $validAttributes = Get-PSFConfigValue -FullName 'FortigateManager.ValidAttr.FirewallAddress'
    }
    process {
        $Address | ForEach-Object { $addressList += $_ | ConvertTo-PSFHashtable -Include $validAttributes }
    }
    end {
        if ($addressList.count -gt 1 -and $Name) {
            Stop-PSFFunction -AlwaysWarning -EnableException $EnableException -Message "Usage of -Name and more than one -Address is not permitted"
            return
        }
        $apiCallParameter = @{
            EnableException     = $EnableException
            RevisionNote        = $RevisionNote
            Connection          = $Connection
            LoggingAction       = "Update-FMAddress"
            LoggingActionValues = @($addressList.count, $explicitADOM, $Name)
            method              = "update"
            Path                = "/pm/config/adom/$explicitADOM/obj/firewall/address"
            Parameter           = @{
                "data" = $addressList
            }
        }
        if ($Name) {
            $apiCallParameter.Path = "/pm/config/adom/$explicitADOM/obj/firewall/address/$($Name|ConvertTo-FMUrlPart)"
            # if name is given 'data' does only accept one object but no array
            $apiCallParameter.Parameter.data = $addressList[0]
        }
        $result = Invoke-FMAPI @apiCallParameter
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }
}

function Update-FMAddressGroup {
    <#
    .SYNOPSIS
    Updates an existing address group.
 
    .DESCRIPTION
    Updates an existing address group.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Name
    The name of the address group to be changed (neccessary if the 'name' property itself changes)
 
    .PARAMETER AddressGroup
    The new address group, generated e.g. by using New-FMObjAddress or Get-FMAddress
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    $testGroup = Get-FMAddressGroup -Connection $Connection -Filter "name -eq Dummy-Group"| ConvertTo-PSFHashtable
    $testGroup.comment = "Modified at $(Get-Date)"
    $testGroup | Update-fmaddressgroup -verbose
 
    Sets the comment field (must exist or you need to have e.g. Add-Member.)
 
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        [object[]]$AddressGroup,
        [string]$Name,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $groupList = @()
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        $validAttributes = Get-PSFConfigValue -FullName 'FortigateManager.ValidAttr.FirewallAddressGroups'
        $validAttributesDynaMapping = Get-PSFConfigValue -FullName 'FortigateManager.ValidAttr.FirewallAddressGroups.dynamicMapping'
    }
    process {
        $AddressGroup | ForEach-Object {
            $currentHash = $_ | ConvertTo-PSFHashtable -Include $validAttributes
            if ($currentHash.ContainsKey('dynamic_mapping')) {
                Write-PSFMessage "Modify/clean dynamic_mapping attribute"
                $currentHash.dynamic_mapping = $currentHash.dynamic_mapping | ConvertTo-PSFHashtable -Include $validAttributesDynaMapping
            }
            $groupList += $currentHash
        }
    }
    end {
        if ($groupList.count -gt 1 -and $Name) {
            Stop-PSFFunction -AlwaysWarning -EnableException $EnableException -Message "Usage of -Name and more than one -AddressGroup is not permitted"
            return
        }
        $apiCallParameter = @{
            EnableException     = $EnableException
            RevisionNote        = $RevisionNote
            Connection          = $Connection
            LoggingAction       = "Update-FMAddressGroup"
            LoggingActionValues = @($groupList.count, $explicitADOM, $Name)
            method              = "update"
            Path                = "/pm/config/adom/$explicitADOM/obj/firewall/addrgrp"
            Parameter           = @{
                "data" = $groupList
            }
        }
        if ($Name) {
            $apiCallParameter.Path = "/pm/config/adom/$explicitADOM/obj/firewall/addrgrp/$($Name|ConvertTo-FMUrlPart)"
            # if name is given 'data' does only accept one object but no array
            $apiCallParameter.Parameter.data = $groupList[0]
        }
        $result = Invoke-FMAPI @apiCallParameter
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }
}

function Update-FMAddressGroupMember {
    <#
    .SYNOPSIS
    Updates the members of an existing address group.
 
    .DESCRIPTION
    Updates the members of an existing address group.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Name
    The name of the address group(s) to be changed.
 
    .PARAMETER Member
    The members to be removed or added.
 
    .PARAMETER NoneMember
    If after a removal no member is left the address with this name
    (default="none") will be added to the empty member list
 
    .PARAMETER Action
    remove or add.
 
    .PARAMETER Scope
    Can be used if the members of a specific dynamic_mapping should be changed.
    Format: Array of Hashtables, @{name="MyFirewall";vdom="MyVDOM"}
    or '*' if all available scopes should be changed
 
 
    .PARAMETER ActionMap
    Array of hashtable/PSCustomObjects with the attributes
    -addrGrpName
    -addrName
    -action
    -scope
    for performing multiple changes in one API call.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    Update-FMAddressGroupMember -Action add -Name "PESTER Single $pesterGUID" -Member "PESTER 3 $pesterGUID"
 
    adds the address "PESTER 3 $pesterGUID" to the addressgroup "PESTER Single $pesterGUID"
 
    .EXAMPLE
    Update-FMAddressGroupMember -Action add -Name @("PESTER Single 1","PESTER Single 2") -Member "PESTER 4"
 
    adds the address "PESTER 4" to the addressgroups "PESTER Single 1","PESTER Single 2"
 
    .EXAMPLE
    Update-FMAddressGroupMember -Action add -Name "MyGroup" -Member "MyAddress" -Scope @{name="MyFirewall";vdom="MyVDOM"}
 
    Adds the address to the addressgroup but not for the default member collection but to the dynamic_mapping with the given scope.
 
    .EXAMPLE
     $actionMap = @(
        @{
            addrGrpName = "PESTER Twin1 $pesterGUID"
            addrName = "PESTER 6 $pesterGUID"
            action = "add"
        },
        @{
            addrGrpName = "PESTER Twin1 $pesterGUID"
            addrName = "PESTER 1 $pesterGUID"
            action = "remove"
        },
        @{
            addrGrpName = "PESTER Twin2 $pesterGUID"
            addrName = "PESTER 7 $pesterGUID"
            action = "add"
        },
        @{
            addrGrpName = "PESTER Twin2 $pesterGUID"
            addrName = "PESTER 2 $pesterGUID"
            action = "remove"
        }
    )
    Update-FMAddressGroupMember -ActionMap $actionMap
 
    Performs multiple additions/removals in one API call
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "default")]
        [string[]]$Name,
        [parameter(mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "default")]
        [string[]]$Member,
        [parameter(mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = "default")]
        [ValidateSet("remove", "add")]
        [string]$Action,
        [parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = "default")]
        [object[]]$Scope,
        [parameter(mandatory = $true, ParameterSetName = "table")]
        [object[]]$ActionMap,
        [string]$NoneMember = "none",
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        $internalActionMap = @()
        $modifiedAddrGroups = @{}
        $availableScopes = @()
    }
    process {
        if ($PSCmdlet.ParameterSetName -eq 'default') {
            Write-PSFMessage "`$Scope=$($Scope|ConvertTo-Json -Compress)"
            foreach ($group in $Name) {
                foreach ($memberName in $Member) {
                    if ($Scope) {
                        if ($Scope -eq '*') {
                            Write-PSFMessage "Scope should be all available scopes"
                            if ($availableScopes.Count -eq 0) {
                                Write-PSFMessage "Query existing scopes from device info"
                                $devInfo = Get-FMDeviceInfo -Option 'object member'
                                $availableScopes = $devInfo."object member" | Select-Object name, vdom | Where-Object { $_.vdom }
                                $Scope = $availableScopes
                            }
                        }
                        foreach ($singleScope in $Scope) {
                            $internalActionMap += @{
                                addrGrpName = $group
                                addrName    = $memberName
                                action      = $Action
                                scope       = $singleScope
                            }
                        }
                    }
                    else {
                        $internalActionMap += @{
                            addrGrpName = $group
                            addrName    = $memberName
                            action      = $Action
                        }
                    }
                }
            }
        }
    }
    end {
        Write-PSFMessage "`$ActionMap=$($ActionMap|ConvertTo-Json -Depth 4)"
        Write-PSFMessage "`$internalActionMap=$($internalActionMap|ConvertTo-Json -Depth 4)"
        if ($ActionMap) {
            Write-PSFMessage "Using ActionMap from parameter"
            $groupedActions = $ActionMap | Group-Object -Property addrGrpName, scope
        }
        else {
            $groupedActions = $internalActionMap | Group-Object -Property addrGrpName, scope
            Write-PSFMessage "Using internalActionMap from manual mapping"
        }
        # Write-PSFMessage "`$groupedActions=$($groupedActions|ConvertTo-Json -Depth 4)"
        foreach ($actionGroup in $groupedActions) {
            $addressGroupName = $actionGroup.Values[0]
            $dynaScope = $actionGroup.Values[1]
            # Write-PSFMessage "`$actionGroup=$($actionGroup|ConvertTo-Json -Depth 4)"
            Write-PSFMessage "Modify AddressGroup $addressGroupName"
            Write-PSFMessage "`$dynaScope=$dynaScope"
            Write-PSFMessage "`$dynaScope=null=$($null -eq $dynaScope)"

            if ($modifiedAddrGroups.ContainsKey($addressGroupName)) {
                $group = $modifiedAddrGroups.$addressGroupName
            }
            else {
                $group = Get-FMAddressGroup -Connection $Connection  -ADOM $explicitADOM -Filter "name -eq $addressGroupName" -Option 'scope member' -Fields name, member -LoggingLevel Verbose
            }
            # Write-PSFMessage "`$group= $($group.member.gettype())"
            if ($null -eq $dynaScope) {
                $members = [System.Collections.ArrayList]($group.member)
            }
            else {
                Write-PSFMessage "Verwende Scope $($dynaScope.name) VDOM $($dynaScope.vdom)"
                Write-PSFMessage "`$group= $($group |ConvertTo-Json -Depth 5)"
                $dynamapping = $group.dynamic_mapping | Where-Object { $_._scope.name -eq $dynaScope.name -and $_._scope.vdom -eq $dynaScope.vdom }
                if ($null -eq $dynamapping) {
                    Write-PSFMessage "dynamic_mapping does not exist, create it"
                    $dynamapping = New-FMObjDynamicAddressGroupMapping -Scope $dynaScope -Member "none"
                    $group.dynamic_mapping = @($dynamapping)
                    # $group | Add-Member -name dynamic_mapping -Value @($dynamapping) -MemberType NoteProperty
                }
                $members = [System.Collections.ArrayList]($dynamapping.member)
            }
            foreach ($memberAction in $actionGroup.Group) {
                # Write-PSFMessage "`$memberAction=$($memberAction|ConvertTo-Json -Depth 4)"
                Write-PSFMessage "$($memberAction.action) $($memberAction.addrName)"
                if ($memberAction.action -eq 'add') {
                    $members.Add($memberAction.addrName) | Out-Null
                }
                else {
                    $members.Remove($memberAction.addrName) | Out-Null
                }
            }
            $oldMembers = $group.member
            if ($members.Count -eq 0) {
                Write-PSFMessage "Add member $NoneMember to empty member collection"
                $members.Add($NoneMember)
            }
            elseif ($members.Contains($NoneMember)) {
                Write-PSFMessage "Remove member $NoneMember from now not empty member collection"
                $members.Remove($NoneMember)
            }
            Write-PSFMessage "OldMembers= $($oldMembers -join ',')"
            Write-PSFMessage "NewMembers= $($members -join ',')"
            if ($dynaScope) {
                Write-PSFMessage "Speichere Scope"
                $dynamapping.member = $members.ToArray()
            }
            else {
                $group.member = $members.ToArray()
            }

            $modifiedAddrGroups.$addressGroupName = $group
        }
        $modifiedAddrGroups.values | Update-FMAddressGroup -Connection $Connection -ADOM $explicitADOM -EnableException $EnableException -RevisionNote $RevisionNote
    }
}

function Update-FMFirewallPolicy {
    <#
    .SYNOPSIS
    Adds new firewall policies to the given ADOM and policy package.
 
    .DESCRIPTION
    Adds new firewall policies to the given ADOM and policy package.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Package
    The name of the policy package
 
    .PARAMETER Policy
    The new policy, generated e.g. by using New-FMObjAddress
 
    .PARAMETER PolicyID
    The ID of the policies to be modified with the Attribute values
 
    .PARAMETER Attribute
    The attributes to be modified for the policies with the IDs of the parameter PolicyID
 
    .PARAMETER Overwrite
    If used and an policy with the given name already exists the data will be overwritten.
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    Update-FMFirewallPolicy -Connection $Connection -Adom $explicitADOM -Package $Package -PolicyId 4711 -Attribute @{status = 'disable'}
 
    Sets the status attribute of the policy 4711 to disable
 
    .EXAMPLE
    $policy = Get-FMFirewallPolicy -Package $packageName -Filter "name -like %$pesterGUID"
    $policy.service = "HTTP"
    $policy | Update-FMFirewallPolicy -Package $packageName
 
    Updates the service of the queried policy rule.
 
    Later
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ParameterSetName = "default")]
        [parameter(mandatory = $true, ParameterSetName = "multiUpdate")]
        [PSFramework.TabExpansion.PsfArgumentCompleterAttribute("FortigateManager.FirewallPackage")]
        [string]$Package,
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        [object[]]$Policy,
        [parameter(mandatory = $true, ValueFromPipeline = $false, ParameterSetName = "multiUpdate")]
        [Int64[]]$PolicyID,
        [parameter(mandatory = $true, ValueFromPipeline = $false, ParameterSetName = "multiUpdate")]
        [hashtable]$Attribute,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $policyList = @()
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        $validAttributes = Get-PSFConfigValue -FullName 'FortigateManager.ValidAttr.FirewallPolicy'
    }
    process {
        switch ($PSCmdlet.ParameterSetName) {
            'multiUpdate' {  }
            Default {
                $Policy | ForEach-Object {
                    $policyList += $_ | ConvertTo-PSFHashtable -Include $validAttributes
                }
            }
        }
    }
    end {
        switch ($PSCmdlet.ParameterSetName) {
            'multiUpdate' {
                Write-PSFMessage "Update $($PolicyID.count) Policies with $($Attribute.count) new attributes"
                $Attribute = $Attribute | ConvertTo-PSFHashtable -Include $validAttributes
                $apiCallParameter = @{
                    EnableException     = $EnableException
                    RevisionNote        = $RevisionNote
                    Connection          = $Connection
                    LoggingAction       = "Update-FMFirewallPolicy"
                    LoggingActionValues = @($PolicyID.count, $explicitADOM, $Package)
                    method              = "update"
                    Parameter           = @()
                }
                foreach ($polId in $PolicyID) {
                    $apiCallParameter.Parameter += @{
                        url          = "/pm/config/adom/$explicitADOM/pkg/$Package/firewall/policy/$polId"
                        RevisionNote = $RevisionNote
                        data         = $Attribute
                    }
                }

                $result = Invoke-FMAPI @apiCallParameter
            }
            Default {
                $apiCallParameter = @{
                    EnableException     = $EnableException
                    RevisionNote        = $RevisionNote
                    Connection          = $Connection
                    LoggingAction       = "Update-FMFirewallPolicy"
                    LoggingActionValues = @($policyList.count, $explicitADOM, $Package)
                    method              = "update"
                    Path                = "/pm/config/adom/$explicitADOM/pkg/$Package/firewall/policy"
                    Parameter           = @{
                        "data" = $policyList
                    }
                }
                $result = Invoke-FMAPI @apiCallParameter
            }
        }
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }
}

function Update-FMFirewallService {
    <#
    .SYNOPSIS
    Adds new firewall services to the given ADOM.
 
    .DESCRIPTION
    Adds new firewall services to the given ADOM.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Name
    The name of the service to be changed (neccessary if the 'name' property itself changes)
    This is the *old* Name of the existing object! The new name has to be set in the object itself.
 
    .PARAMETER Service
    The new service, generated e.g. by using New-FMObjFirewallService
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    # Read some input in the format [IP]/[Subnet-Mask]
    $missingAddresses=Get-Content "PATH TO SOME FILE"
    # prepare a temporary Array
    $newFMAddresses=@()
    # Fill the array with the needed structure
    foreach ($newAddress in $missingAddresses) {
        $newFMAddresses += New-FMObjAddress -Name "$newAddress" -Type ipmask -Subnet "$newAddress" -Comment "Created for testing purposes"
    }
    # Lock + Add + Commit + Unlock
    Lock-FMAdom -Connection $connection
    $newFMAddresses | Add-FMaddress -connection $connection
    Publish-FMAdomChange -Connection $connection
    UnLock-FMAdom -Connection $connection
 
    Read som subet information and add the subnets to the fortigate manager
    .NOTES
    General notes
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        [object[]]$Service,
        [string]$Name,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $serviceList = @()
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        $validAttributes = Get-PSFConfigValue -FullName 'FortigateManager.ValidAttr.FirewallService'
    }
    process {
        $Service | ForEach-Object { $serviceList += $_ | ConvertTo-PSFHashtable -Include $validAttributes }
    }
    end {
        if ($serviceList.count -gt 1 -and $Name) {
            Stop-PSFFunction -AlwaysWarning -EnableException $EnableException -Message "Usage of -Name and more than one -Service is not permitted"
            return
        }
        $apiCallParameter = @{
            RevisionNote        = $RevisionNote
            EnableException     = $EnableException
            Connection          = $Connection
            LoggingAction       = "Update-FMFirewallService"
            LoggingActionValues = @($serviceList.count, $explicitADOM, $Name)
            method              = "update"
            Path                = "/pm/config/adom/$explicitADOM/obj/firewall/service/custom"
            Parameter           = @{
                "data" = $serviceList
            }
        }
        if ($Name) {
            $apiCallParameter.Path = "/pm/config/adom/$explicitADOM/obj/firewall/service/custom/$($Name|ConvertTo-FMUrlPart)"
            # if name is given 'data' does only accept one object but no array
            $apiCallParameter.Parameter.data = $serviceList[0]
        }
        $result = Invoke-FMAPI @apiCallParameter
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }
}

function Update-FMInterface {
    <#
    .SYNOPSIS
    Updates an existing Interfacees.
 
    .DESCRIPTION
    Updates an existing Interfacees.
 
    .PARAMETER Connection
    The API connection object.
 
    .PARAMETER ADOM
    The (non-default) ADOM for the requests.
 
    .PARAMETER Name
    The name of the Interface to be changed (neccessary if the 'name' property itself changes)
    This is the *old* Name of the existing object! The new name has to be set in the object itself.
 
    .PARAMETER Interface
    The new Interface, generated e.g. by using New-FMObjInterface or Get-FMInterface
 
    .PARAMETER RevisionNote
    The change note which should be saved for this revision, see about_RevisionNote
 
      .PARAMETER EnableException
    Should Exceptions been thrown?
 
    .EXAMPLE
    $testInterface=Get-FMInterface -filter "name -eq MyDUMMY" -verbose
    $testInterface.comment="Modified at $(Get-Date)"
    $testInterface | Update-fmInterface
 
    Sets the comment field (must exist or you need to have e.g. Add-Member.)
 
    .NOTES
    If an Interface object does not contain every attribute which is already set,
    then the existing values will be kept.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [parameter(Mandatory = $false)]
        $Connection = (Get-FMLastConnection),
        [string]$ADOM,
        [parameter(mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "default")]
        [object[]]$Interface,
        [string]$Name,
        [string]$RevisionNote,
        [bool]$EnableException = $true
    )
    begin {
        $InterfaceList = @()
        $explicitADOM = Resolve-FMAdom -Connection $Connection -Adom $ADOM
        Write-PSFMessage "`$explicitADOM=$explicitADOM"
        $validAttributes = Get-PSFConfigValue -FullName 'FortigateManager.ValidAttr.FirewallInterface'
    }
    process {
        $Interface | ForEach-Object { $InterfaceList += $_ | ConvertTo-PSFHashtable -Include $validAttributes }
    }
    end {
        if ($InterfaceList.count -gt 1 -and $Name) {
            Stop-PSFFunction -AlwaysWarning -EnableException $EnableException -Message "Usage of -Name and more than one -Interface is not permitted"
            return
        }
        $apiCallParameter = @{
            RevisionNote        = $RevisionNote
            EnableException     = $EnableException
            Connection          = $Connection
            LoggingAction       = "Update-FMInterface"
            LoggingActionValues = @($InterfaceList.count, $explicitADOM, $Name)
            method              = "update"
            Path                = "/pm/config/adom/$explicitADOM/obj/dynamic/interface"
            Parameter           = @{
                "data" = $InterfaceList
            }
        }
        if ($Name) {
            $apiCallParameter.Path = "/pm/config/adom/$explicitADOM/obj/firewall/Interface/$($Name|ConvertTo-FMUrlPart)"
            # if name is given 'data' does only accept one object but no array
            $apiCallParameter.Parameter.data = $InterfaceList[0]
        }
        $result = Invoke-FMAPI @apiCallParameter
        if (-not $EnableException) {
            return ($null -ne $result)
        }
    }
}

<#
This is an example configuration file
 
By default, it is enough to have a single one of them,
however if you have enough configuration settings to justify having multiple copies of it,
feel totally free to split them into multiple files.
#>


<#
# Example Configuration
Set-PSFConfig -Module 'FortigateManager' -Name 'Example.Setting' -Value 10 -Initialize -Validation 'integer' -Handler { } -Description "Example configuration setting. Your module can then use the setting using 'Get-PSFConfigValue'"
#>


Set-PSFConfig -Module 'FortigateManager' -Name 'Import.DoDotSource' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be dotsourced on import. By default, the files of this module are read as string value and invoked, which is faster but worse on debugging."
Set-PSFConfig -Module 'FortigateManager' -Name 'Import.IndividualFiles' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be imported individually. During the module build, all module code is compiled into few files, which are imported instead by default. Loading the compiled versions is faster, using the individual files is easier for debugging and testing out adjustments."
Set-PSFConfig -Module 'FortigateManager' -Name 'Logging.Api' -Value "Host" -Initialize -Validation string -Description "Level Invoke-API should log to"

# For quick collection of valid attibutes copy an example hashtable to the clipboard and run
# Get-Clipboard | ConvertFrom-Json | ConvertTo-PSFHashtable | Select-Object -ExpandProperty keys | wrap "'" "'" | join ',' | Set-Clipboard -PassThru



Set-PSFConfig -Module 'FortigateManager' -Name 'ValidAttr.FirewallService' -Value @('fabric-object', 'tcp-portrange', 'visibility', 'tcp-halfclose-timer', 'iprange', 'name', 'udp-idle-timer', 'app-category', 'icmptype', 'udp-portrange', 'comment', 'session-ttl', 'helper', 'protocol', 'icmpcode', 'tcp-rst-timer', 'color', 'tcp-timewait-timer', 'sctp-portrange', 'protocol-number', 'application', 'check-reset-range', 'tcp-halfopen-timer', 'app-service-type', 'fqdn', 'category', 'proxy') -Initialize -Validation stringarray -Description "Which attributes are valid for firewall Services?"
Set-PSFConfig -Module 'FortigateManager' -Name 'ValidAttr.FirewallPolicy' -Value @("action", "anti-replay", "application-list", "auth-cert", "auth-path", "auth-redirect-addr", "auto-asic-offload", "av-profile", "block-notification", "captive-portal-exempt", "capture-packet", "cifs-profile", "comments", "custom-log-fields", "decrypted-traffic-mirror", "delay-tcp-npu-session", "diffserv-forward", "diffserv-reverse", "diffservcode-forward", "diffservcode-rev", "disclaimer", "dlp-profile", "dnsfilter-profile", "dsri", "dstaddr", "dstaddr-negate", "dstaddr6", "dstintf", "dynamic-shaping", "email-collect", "emailfilter-profile", "fec", "file-filter-profile", "firewall-session-dirty", "fixedport", "fsso-agent-for-ntlm", "fsso-groups", "geoip-anycast", "geoip-match", "global-label", "groups", "gtp-profile", "http-policy-redirect", "icap-profile", "identity-based-route", "inbound", "inspection-mode", "internet-service", "internet-service-custom", "internet-service-custom-group", "internet-service-group", "internet-service-name", "internet-service-negate", "internet-service-src", "internet-service-src-custom", "internet-service-src-custom-group", "internet-service-src-group", "internet-service-src-name", "internet-service-src-negate", "ippool", "ips-sensor", "label", "logtraffic", "logtraffic-start", "match-vip", "match-vip-only", "name", "nat", "nat46", "nat64", "natinbound", "natip", "natoutbound", "np-acceleration", "ntlm", "ntlm-enabled-browsers", "ntlm-guest", "outbound", "passive-wan-health-measurement", "per-ip-shaper", "permit-any-host", "permit-stun-host", "pfcp-profile", "policyid", "policy-expiry", "policy-expiry-date", "poolname", "poolname6", "profile-group", "profile-protocol-options", "profile-type", "radius-mac-auth-bypass", "redirect-url", "replacemsg-override-group", "reputation-direction", "reputation-minimum", "rtp-addr", "rtp-nat", "schedule", "schedule-timeout", "scope member", "sctp-filter-profile", "send-deny-packet", "service", "service-negate", "session-ttl", "sgt", "sgt-check", "src-vendor-mac", "srcaddr", "srcaddr-negate", "srcaddr6", "srcintf", "ssh-filter-profile", "ssh-policy-redirect", "ssl-ssh-profile", "status", "tcp-mss-receiver", "tcp-mss-sender", "tcp-session-without-syn", "timeout-send-rst", "tos", "tos-mask", "tos-negate", "traffic-shaper", "traffic-shaper-reverse", "users", "utm-status", "videofilter-profile", "vlan-cos-fwd", "vlan-cos-rev", "vlan-filter", "voip-profile", "vpntunnel", "waf-profile", "wanopt", "wanopt-detection", "wanopt-passive-opt", "wanopt-peer", "wanopt-profile", "wccp", "webcache", "webcache-https", "webfilter-profile", "webproxy-forward-server", "webproxy-profile", "ztna-ems-tag", "ztna-geo-tag", "ztna-status") -Initialize -Validation stringarray -Description "Which attributes are valid for firewall Policy?"
Set-PSFConfig -Module 'FortigateManager' -Name 'ValidAttr.FirewallAddress' -Value @('tag-detection-level', 'end-ip', 'fabric-object', 'name', 'sub-type', 'fsso-group', 'node-ip-only', 'subnet-name', 'sdn-tag', 'dynamic_mapping', 'filter', 'comment', 'organization', 'sdn', '_image-base64', 'sdn-addr-type', 'tagging', 'policy-group', 'macaddr', 'obj-type', 'dirty', 'allow-routing', 'color', 'country', 'interface', 'wildcard', 'start-ip', 'epg-name', 'obj-tag', 'wildcard-fqdn', 'subnet', 'type', 'fqdn', 'associated-interface', 'list', 'tag-type', 'tenant', 'clearpass-spt', 'cache-ttl') -Initialize -Validation stringarray -Description "Which attributes are valid for firewall addresses?"
Set-PSFConfig -Module 'FortigateManager' -Name 'ValidAttr.FirewallAddressGroups' -Value @('color', 'dynamic_mapping', 'member', 'type', 'tagging', 'comment', 'category', 'exclude-member', 'name', '_image-base64', 'exclude', 'fabric-object', 'allow-routing') -Initialize -Validation stringarray -Description "Which attributes are valid for firewall address groups?"
Set-PSFConfig -Module 'FortigateManager' -Name 'ValidAttr.FirewallAddressGroups.dynamicMapping' -Value @('category', 'global-object', 'fabric-object', 'exclude-member', 'color', 'allow-routing', 'visibility', '_image-base64', 'type', '_scope', 'tags', 'uuid', 'member', 'comment', 'exclude') -Initialize -Validation stringarray -Description "Which attributes are valid for firewall address groups?"
Set-PSFConfig -Module 'FortigateManager' -Name 'ValidAttr.FirewallInterface' -Value @('color', 'zone-only', 'description', 'ingress-shaping-profile', 'defmap-zonemember', 'single-intf', 'platform_mapping', 'egress-shaping-profile', 'defmap-intf', 'wildcard-intf', 'dynamic_mapping', 'name', 'default-mapping', 'defmap-intrazone-deny', 'wildcard') -Initialize -Validation stringarray -Description "Which attributes are valid for firewall interfaces?"


<#
Stored scriptblocks are available in [PsfValidateScript()] attributes.
This makes it easier to centrally provide the same scriptblock multiple times,
without having to maintain it in separate locations.
 
It also prevents lengthy validation scriptblocks from making your parameter block
hard to read.
 
Set-PSFScriptblock -Name 'FortigateManager.ScriptBlockName' -Scriptblock {
     
}
#>


<#
# Example:
Register-PSFTeppScriptblock -Name "FortigateManager.alcohol" -ScriptBlock { 'Beer','Mead','Whiskey','Wine','Vodka','Rum (3y)', 'Rum (5y)', 'Rum (7y)' }
#>


Register-PSFTeppScriptblock -Name "FortigateManager.FirewallPackage" -ScriptBlock {
    try {
        $connection = (Get-FMLastConnection)
        if($connection) {
            return Get-FMPolicyPackage -LoggingLevel Debug | Select-Object -ExpandProperty name | ForEach-Object { "`"$_`"" }
        }
    }
    catch {
        return "Error"
    }
}


<#
# Example:
Register-PSFTeppArgumentCompleter -Command Get-Alcohol -Parameter Type -Name FortigateManager.alcohol
#>


New-PSFLicense -Product 'FortigateManager' -Manufacturer 'Sascha Spiekermann' -ProductVersion $script:ModuleVersion -ProductType Module -Name GPL -Version "3.0.0.0" -Date (Get-Date "2020-09-21") -Text @"
GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007
 
 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.
 
                            Preamble
 
  The GNU General Public License is a free, copyleft license for
software and other kinds of works.
 
  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
 
  When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
 
  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
 
  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
 
  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
 
  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
 
  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
 
  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
 
  The precise terms and conditions for copying, distribution and
modification follow.
 
                       TERMS AND CONDITIONS
 
  0. Definitions.
 
  "This License" refers to version 3 of the GNU General Public License.
 
  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
 
  "The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
 
  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
 
  A "covered work" means either the unmodified Program or a work based
on the Program.
 
  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
 
  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
 
  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
 
  1. Source Code.
 
  The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
 
  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
 
  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
 
  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
 
  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
 
  The Corresponding Source for a work in source code form is that
same work.
 
  2. Basic Permissions.
 
  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
 
  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
 
  Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
 
  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
 
  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
 
  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
 
  4. Conveying Verbatim Copies.
 
  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
 
  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
 
  5. Conveying Modified Source Versions.
 
  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
 
    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.
 
    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7. This requirement modifies the requirement in section 4 to
    "keep intact all notices".
 
    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy. This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged. This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.
 
    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.
 
  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
 
  6. Conveying Non-Source Forms.
 
  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
 
    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.
 
    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.
 
    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source. This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.
 
    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge. You need not require recipients to copy the
    Corresponding Source along with the object code. If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source. Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.
 
    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.
 
  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
 
  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
 
  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
 
  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
 
  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
 
  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
 
  7. Additional Terms.
 
  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
 
  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
 
  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
 
    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or
 
    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or
 
    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or
 
    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or
 
    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or
 
    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.
 
  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
 
  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
 
  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
 
  8. Termination.
 
  You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
 
  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
 
  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
 
  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
 
  9. Acceptance Not Required for Having Copies.
 
  You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
 
  10. Automatic Licensing of Downstream Recipients.
 
  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
 
  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
 
  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
 
  11. Patents.
 
  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
 
  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
 
  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
 
  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
 
  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
 
  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
 
  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
 
  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
 
  12. No Surrender of Others' Freedom.
 
  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
 
  13. Use with the GNU Affero General Public License.
 
  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
 
  14. Revised Versions of this License.
 
  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
 
  Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
 
  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
 
  Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
 
  15. Disclaimer of Warranty.
 
  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
 
  16. Limitation of Liability.
 
  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
 
  17. Interpretation of Sections 15 and 16.
 
  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
 
                     END OF TERMS AND CONDITIONS
 
            How to Apply These Terms to Your New Programs
 
  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
 
  To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
 
    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year> <name of author>
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program. If not, see <https://www.gnu.org/licenses/>.
 
Also add information on how to contact you by electronic and paper mail.
 
  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
 
    <program> Copyright (C) <year> <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.
 
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
 
  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
 
  The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
"@

#endregion Load compiled code