Public/Object/Construct-PanObject.ps1

function Construct-PanObject {
<#
.SYNOPSIS
Creates a new object locally, NOT on the Device. Use in place of calling ::new() constructors.
.DESCRIPTION
Creates a new object locally, NOT on the Device. Use in place of calling ::new() constructors.
Workaround to PowerShell GitHub issue 2449.
.NOTES
Most users should NEVER have to use this cmdlet and should almost always use Set- instead.
It is present for advanced scripting purposes only as a workaround to PowerShell issue 2449 affecting
script based modules and class availability outside the module.

Intended to be used to create objects instead of calling their ::new() class constructors directly as
the class constructors are not available outside the module.
PowerShell GitHub issue 2449 https://github.com/PowerShell/PowerShell/issues/2449

Uses an unapproved Construct- verb instead of New- by design to distinguish its unique semantics.

:: Usage ::
# Instead of this (does not work given issue 2449)
$A = [PanAddress]::new($Name,$Device,$Location)
# Do this
$A = Construct-PanAddress -Name $Name -Device $Device -Location $Location

# Instead of this
$S = [PanService]::new($Name,$Device,$Location)
# Do this
$S = Construct-PanService -Name $Name -Device $Device -Location $Location

Creates a minimally viable object. Finish the object by assigning values to the object's properties.

# For a [PanAddress], like below
$A = Construct-PanAddress -Name $Name -Device $Device -Location $Location
$A.Type = 'ip-netmask'
$A.Value = '10.0.0.100'
.INPUTS
None
.OUTPUTS
PanAddress
PanService
PanAddressGoup
PanServiceGroup
.EXAMPLE
$D = Get-PanDevice -Name "fw.lab.local"
$A = Construct-PanAddress "MyAddress" -Device $D -Location "vsys1"
$A.Type = 'ip-netmask'
$A.Value = '10.0.0.100'
$A.Description = 'My Description'
$A.Tag = @('review')
$A | Set-PanAddress

The example can be used to create an object *without* immediately creating/updating it on the Device.
Mostly for advanced scripting purposes.

For normal usage, just use Set-PanAddress directly like below which will create/update the PanAddress on the Device.

Set-PanAddress -Device $D -Location "vsys1" -Name "MyAddress" -Type "ip-netmask" -Value "10.0.0.100" -Description "My Description" -Tag @("review")
#>

    [CmdletBinding()]
    param(
        [parameter(ParameterSetName='NonXML',Mandatory=$true,HelpMessage='PanDevice')]
        [parameter(ParameterSetName='XML',Mandatory=$true,HelpMessage='PanDevice')]
        [PanDevice] $Device,
        [parameter(ParameterSetName='NonXML',Mandatory=$true,HelpMessage='Case-sensitive location: vsys1, shared, DeviceGroupA, etc.')]
        [String] $Location,
        [parameter(ParameterSetName='NonXML',Mandatory=$true,HelpMessage='Case-sensitive name of object')]
        [String] $Name,
        [parameter(ParameterSetName='XML',Mandatory=$true,HelpMessage='Object full XPath including entry[@name]')]
        [String] $XPath,
        [parameter(ParameterSetName='XML',Mandatory=$true,HelpMessage='XmlDocument representing object from <entry> to </entry>')]
        [System.Xml.XmlDocument] $XDoc
    )

    Begin {
        # Propagate -Debug and -Verbose to this module function, https://tinyurl.com/y5dcbb34
        if($PSBoundParameters.Debug) { $DebugPreference = 'Continue' }
        if($PSBoundParameters.Verbose) { $VerbosePreference = 'Continue' }
        # Announce
        Write-Debug ('{0} (as {1}):' -f $MyInvocation.MyCommand.Name,$MyInvocation.InvocationName)

        # Terminating error if called directly. Use a supported alias.
        if($MyInvocation.InvocationName -eq $MyInvocation.MyCommand.Name) {
            $Alias = (Get-Alias | Where-Object {$_.ResolvedCommandName -eq $($MyInvocation.MyCommand.Name)} | Select-Object -ExpandProperty Name) -join ','
            Write-Error ('{0} called directly. {0} MUST be called by an alias: {1}' -f $MyInvocation.MyCommand.Name,$Alias) -ErrorAction Stop
        }
    } # Begin Block

    Process {
        if($PSCmdlet.ParameterSetName -eq 'NonXML') {
            Write-Debug ('{0} (as {1}): Device: {2} Location: {3} Name: {4}' -f 
                $MyInvocation.MyCommand.Name, $MyInvocation.InvocationName, $PSBoundParameters.Device.Name, $PSBoundParameters.Location, $PSBoundParameters.Name)
            # Update Location if past due
            if($PSBoundParameters.Device.LocationUpdated.AddSeconds($Global:PanDeviceLocRefSec) -lt (Get-Date)) { Update-PanDeviceLocation -Device $PSBoundParameters.Device }
            
            switch($MyInvocation.InvocationName) {
                # Call constructors *not requiring* XML content
                'Construct-PanAddress'      { [PanAddress]::new($PSBoundParameters.Device,$PSBoundParameters.Location,$PSBoundParameters.Name); continue }
                'Construct-PanService'      { [PanService]::new($PSBoundParameters.Device,$PSBoundParameters.Location,$PSBoundParameters.Name); continue }
                'Construct-PanAddressGroup' { [PanAddressGroup]::new($PSBoundParameters.Device,$PSBoundParameters.Location,$PSBoundParameters.Name); continue }
                'Construct-PanServiceGroup' { [PanServiceGroup]::new($PSBoundParameters.Device,$PSBoundParameters.Location,$PSBoundParameters.Name); continue }
            }
        }
        elseif($PSCmdlet.ParameterSetName -eq 'XML') {
            Write-Debug ('{0} (as {1}): Device: {2} XPath: {3} XDoc: {4}' -f 
                $MyInvocation.MyCommand.Name, $MyInvocation.InvocationName, $PSBoundParameters.Device.Name, $PSBoundParameters.XPath, $PSBoundParameters.XDoc.OuterXml)
            switch($MyInvocation.InvocationName) {
                # Call constructors *requiring* XML content
                'Construct-PanAddress'      { [PanAddress]::new($PSBoundParameters.Device,$PSBoundParameters.XPath,$PSBoundParameters.XDoc); continue }
                'Construct-PanService'      { [PanService]::new($PSBoundParameters.Device,$PSBoundParameters.XPath,$PSBoundParameters.XDoc); continue }
                'Construct-PanAddressGroup' { [PanAddressGroup]::new($PSBoundParameters.Device,$PSBoundParameters.XPath,$PSBoundParameters.XDoc); continue } 
                'Construct-PanServiceGroup' { [PanServiceGroup]::new($PSBoundParameters.Device,$PSBoundParameters.XPath,$PSBoundParameters.XDoc); continue } 
            }
        }
    } # Process block
    
    End {
    } # End block
} # Function