PsLogicAppExtractor.psm1

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

# Detect whether at some level dotsourcing was enforced
$script:doDotSource = Get-PSFConfigValue -FullName PsLogicAppExtractor.Import.DoDotSource -Fallback $false
if ($PsLogicAppExtractor_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 PsLogicAppExtractor.Import.IndividualFiles -Fallback $false
if ($PsLogicAppExtractor_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 'PsLogicAppExtractor' -Language 'en-US'

class Helper {
    Helper([object] $values) {
        if ($values -is [System.Collections.IDictionary]) {
            foreach ($key in $values.Keys) {
                if ($this.PSObject.Properties.Item($key)) {
                    $this.$key = $values[$key]
                }
            }
        }
        else {
            foreach ($property in $values.PSObject.Properties) {
                if ($this.PSObject.Properties.Item($property.Name)) {
                    $this.($property.Name) = $property.Value
                }
            }
        }
    }
}

class Definition: Helper {
    [object] ${$schema}
    [string] $contentVersion
    [object] $parameters
    [object] $triggers
    [object] $actions
    [object] $outputs

    Definition([object] $values) : base($values) { }
}

class Properties : Helper {
    [string] $state
    [Definition] $definition
    [object] $parameters
    [object] $integrationAccount
    [object] $accessControl
    
    Properties([object] $values) : base($values) { }
}

class LogicApp : Helper {
    [string] $type
    [string] $apiVersion
    [string] $name
    [string] $location
    [object] $tags
    [object] $identity
    [Properties] $properties

    LogicApp([object] $values) : base($values) { }
}

class ArmTemplate: Helper {
    [object] ${$schema}
    [string] $contentVersion
    [object] $parameters
    [object] $variables
    [object] $resources
    [object] $outputs
    
    ArmTemplate([object] $values) : base($values) { }
}


<#
.SYNOPSIS
Get the header for a runbook file
 
.DESCRIPTION
Gets the header for a runbook file, containing the sane defaults
 
Allows you to prepare the runbook file as much as possible, based on the parameters that you pass to it
 
.PARAMETER SubscriptionId
Id of the subscription that you want to work against
 
At runtime / execution of Invoke-PsLaExtractor - your current powershell / az cli session either needs to "connected" to the subscription or at least have permissions to work against the subscription
 
Useful when you know upfront what you want to work against, as you don't need to pass the parameter into the Invoke-PsLaExtractor
 
.PARAMETER ResourceGroup
Name of the resource group that you want to work against
 
At runtime / execution of Invoke-PsLaExtractor - your current powershell / az cli session needs to have permissions to work against the resource group
 
Useful when you know upfront what you want to work against, as you don't need to pass the parameter into the Invoke-PsLaExtractor
 
.PARAMETER Name
Name of the logic app, that you want to work against
 
At runtime / execution of Invoke-PsLaExtractor - your current powershell / az cli session needs to have permissions to work against the logic app
 
Useful when you know upfront what you want to work against, as you don't need to pass the parameter into the Invoke-PsLaExtractor
 
.PARAMETER ApiVersion
The ApiVersion that you want the LogicApp to be working against
 
The default value is: "2019-05-01"
 
.PARAMETER IncludePrefixSuffix
Instruct the cmdlet to add the different prefix and suffix options, with the default values that comes with the module
 
This make it easier to make the runbook file work across different environments, without having to worry about prepping different prefix and suffix value prior
 
.EXAMPLE
PS C:\> Get-BuildHeader
 
Creates the bare minimum header for the runbook file
Prepares the Properties object with sane defaults, allowing you to edit them after the file has been created
 
.EXAMPLE
PS C:\> Get-BuildHeader -SubscriptionId "f5608f3d-ab28-49d8-9b4e-1b1f812d12e0" -ResourceGroup "TestRg"
 
Creates the bare minimum header for the runbook file
Prepares the Properties object with SubscriptionId and ResourceGroup, and sane defaults for the remaining objects, allowing you to edit them after the file has been created
 
.EXAMPLE
PS C:\> Get-BuildHeader -Name "TestLogicApp" -ApiVersion "2019-05-01"
 
Creates the bare minimum header for the runbook file
Prepares the Properties object with Name and ApiVersion, and sane defaults for the remaining objects, allowing you to edit them after the file has been created
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Get-BuildHeader {
    [CmdletBinding()]
    param (
        [string] $SubscriptionId,

        [string] $ResourceGroup,

        [string] $Name,

        [string] $ApiVersion = "2019-05-01",

        [switch] $IncludePrefixSuffix
    )

    $res = New-Object System.Collections.Generic.List[System.Object]

    $res.Add("# Object to store the needed parameters for when running the export")
    $res.Add("Properties {")
    
    if ($SubscriptionId) {
        $res.Add('$SubscriptionId = "{0}"' -f $SubscriptionId)
    }
    else {
        $res.Add('$SubscriptionId = $null')
    }

    if ($ResourceGroup) {
        $res.Add('$ResourceGroup = "{0}"' -f $ResourceGroup)
    }
    else {
        $res.Add('$ResourceGroup = $null')
    }
    
    if ($Name) {
        $res.Add('$Name = "{0}"' -f $Name)
    }
    else {
        $res.Add('$Name = ""')
    }

    if ($ApiVersion) {
        $res.Add('$ApiVersion = "{0}"' -f $ApiVersion)
    }
    else {
        $res.Add('$ApiVersion = ""')
    }
    
    if ($IncludePrefixSuffix) {
        $res.Add('$Tag_Prefix = "{0}"' -f $(Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.tag.prefix))
        $res.Add('$Tag_Suffix = "{0}"' -f $(Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.tag.suffix))

        $res.Add('$Parm_Prefix = "{0}"' -f $(Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.parm.prefix))
        $res.Add('$Parm_Suffix = "{0}"' -f $(Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.parm.suffix))

        $res.Add('$Connection_Prefix = "{0}"' -f $(Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.connection.prefix))
        $res.Add('$Connection_Suffix = "{0}"' -f $(Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.connection.suffix))

        $res.Add('$Trigger_Prefix = "{0}"' -f $(Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.trigger.prefix))
        $res.Add('$Trigger_Suffix = "{0}"' -f $(Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.trigger.suffix))
    }

    $res.Add('}')
    #Above line completes the Properties declaration

    $res.Add('')
    $res.Add('# Used to import the needed classes into the powershell session, to help with the export of the Logic App')
    $res.Add('."$(Get-PSFConfigValue -FullName PsLogicAppExtractor.ModulePath.Classes)\PsLogicAppExtractor.class.ps1"')
    $res.Add('')
    $res.Add("# Path variable for all the tasks that is available from the PsLogicAppExtractor module")
    $res.Add('$pathTasks = $(Get-PSFConfigValue -FullName PsLogicAppExtractor.ModulePath.Tasks)')

    $res.Add('')
    $res.Add("# Include all the tasks that is available from the PsLogicAppExtractor module")
    $res.Add("Include `"`$pathTasks\All\All.task.ps1`"")
    $res.Add('')

    $res
}

<#
.SYNOPSIS
Add new parameter to the ARM template
 
.DESCRIPTION
Adds or overwrites an ARM template parameter by the name provided, and allows you to specify the default value, type and the metadata decription
 
.PARAMETER InputObject
The ARM object that you want to work against
 
It has to be a object of the type [ArmTemplate] for it to work properly
 
.PARAMETER Name
Name of the parameter that you want to work against
 
If the parameter exists, the value gets overrided otherwise a new parameter is added to the list of parameters
 
.PARAMETER Type
The type of the ARM template parameter
 
It supports all known types
 
.PARAMETER Value
The default value, that you want to assign to the ARM template parameter
 
.PARAMETER Description
The metadata description that you want to assign to the ARM template parameters
 
.EXAMPLE
PS C:\> Add-ArmParameter -InputObject $armObj -Name "logicAppName" -Type "string" -Value "TestLogicApp"
 
Creates / updates the logicAppName ARM template parameter
Sets the type of the parameter to: string
Sets the default value to: TestLogicApp
 
.EXAMPLE
PS C:\> Add-ArmParameter -InputObject $armObj -Name "logicAppName" -Type "string" -Value "TestLogicApp" -Description "This is the name we extracted from the orignal LogicApp"
 
Creates / updates the logicAppName ARM template parameter
Sets the type of the parameter to: string
Sets the default value to: TestLogicApp
Sets the metadata description
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Add-ArmParameter {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object] $InputObject,

        [Alias('ParameterName')]
        [Parameter(Mandatory = $true)]
        [string] $Name,

        [Parameter(Mandatory = $true)]
        [string] $Type,

        [Parameter(Mandatory = $true)]
        [object] $Value,

        [string] $Description
    )

    if ($Description) {
        $valueObj = $([ordered]@{
                type         = $Type;
                defaultValue = $Value;
                metadata     = [ordered]@{
                    description = $Description
                }
            })

    }
    else {
        $valueObj = $([ordered]@{
                type         = $Type;
                defaultValue = $Value;
            })
    }

    if ($InputObject.parameters.$Name) {
        $InputObject.parameters.$Name = $($valueObj)
    }
    else {
        $InputObject.parameters | Add-Member -MemberType NoteProperty -Name $Name -Value $valueObj
    }
    
    $InputObject
}

<#
.SYNOPSIS
Add new variable to the ARM template
 
.DESCRIPTION
Adds or overwrites an ARM template variable by the name provided, and allows you to specify the value
 
.PARAMETER InputObject
The ARM object that you want to work against
 
It has to be a object of the type [ArmTemplate] for it to work properly
 
.PARAMETER Name
Name of the variable that you want to work against
 
If the variable exists, the value gets overrided otherwise a new variable is added to the list of variables
 
.PARAMETER Value
The value, that you want to assign to the ARM template variable
 
.EXAMPLE
PS C:\> Add-ArmVariable -InputObject $armObj -Name "logicAppName" -Value "TestLogicApp"
 
Creates / updates the logicAppName ARM template variable
Sets the value to: TestLogicApp
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Add-ArmVariable {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object] $InputObject,

        [Alias('VariableName')]
        [Parameter(Mandatory = $true)]
        [string] $Name,

        [Parameter(Mandatory = $true)]
        [object] $Value
    )

    if ($InputObject.variables.$Name) {
        $InputObject.variables.$Name = $($Value)
    }
    else {
        $InputObject.variables | Add-Member -MemberType NoteProperty -Name $Name -Value $($Value)
    }

    $InputObject
}

<#
.SYNOPSIS
Add new parm (parameter) to the LogicApp object
 
.DESCRIPTION
Adds or overwrites a LogicApp parm (parameter) by the name provided, and allows you to specify the default value and the type
 
.PARAMETER InputObject
The ARM object that you want to work against
 
It has to be a object of the type [LogicApp] for it to work properly
 
.PARAMETER Name
Name of the parm (parameter) that you want to work against
 
If the parm (parameter) exists, the value gets overrided otherwise a new parm (parameter) is added to the list of parms (parameters)
 
.PARAMETER Type
The type of the LogicApp parm (parameter)
 
It supports all known types
 
.PARAMETER Value
The default value, that you want to assign to the LogicApp parm (parameter)
 
.EXAMPLE
PS C:\> Add-LogicAppParm -InputObject $lgObj -Name "TriggerQueue" -Type "string" -Value "Inbound"
 
Creates / updates the TriggerQueue LogicApp parm (parameter)
Sets the type of the parameter to: string
Sets the default value to: Inbound
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Add-LogicAppParm {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object] $InputObject,

        [Alias('ParmName')]
        [Parameter(Mandatory = $true)]
        [string] $Name,

        [Parameter(Mandatory = $true)]
        [string] $Type,

        [Parameter(Mandatory = $true)]
        [object] $Value
    )

    $valueObj = $([ordered]@{
            type         = $Type;
            defaultValue = $Value;
        })
        
    if ($InputObject.properties.definition.parameters.$Name) {
        $InputObject.properties.definition.parameters.$Name = $($valueObj)
    }
    else {
        $InputObject.properties.definition.parameters | Add-Member -MemberType NoteProperty -Name $Name -Value $valueObj
    }

    $InputObject
}

<#
.SYNOPSIS
Format the name with the prefix and suffix
 
.DESCRIPTION
Format the name with the prefix and suffix
 
If the passed prefix and suffix is not $null, then they are used
 
Otherwise the cmdlet will default back to the configuration for each type, that is persisted in the configuration store
 
.PARAMETER Type
The type of name that you want to work against
 
Allowed values:
Tag
Connection
Parameter
Parm
 
.PARAMETER Prefix
The prefix that you want to append to the name
 
If empty / $null - then the cmdlet will use the prefix that is stored for the specific type
 
.PARAMETER Suffix
The suffix that you want to append to the name
 
If empty / $null - then the cmdlet will use the suffix that is stored for the specific type
 
.PARAMETER Value
The string value that you want to have the prefix and suffix concatenated with
 
.EXAMPLE
PS C:\> Format-Name -Type "Tag" -Value "CostCenter"
 
Formats the value: CostCenter with the default prefix and suffix for the type: Tag
The default prefix is: tag_
The default suffix is: $null
 
The output will be: tag_CostCenter
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Format-Name {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '')]
    [CmdletBinding()]
    param (
        [ValidateSet('Tag', 'Connection', 'Parameter', 'Parm', 'Trigger')]
        [Parameter(Mandatory = $true)]
        [string] $Type,

        [string] $Prefix,

        [string] $Suffix,

        [Alias('Name')]
        [Parameter(Mandatory = $true)]
        [string] $Value
    )
    
    switch ($Type) {
        "Tag" {
            if ($Prefix -or $Suffix) {
                "$Prefix$Value$Suffix"
            }
            else {
                $Prefix = Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.tag.prefix
                $Suffix = Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.tag.suffix

                "$Prefix$Value$Suffix"
            }
        }
        "Connection" {
            if ($Prefix -or $Suffix) {
                "$Prefix$Value$Suffix"
            }
            else {
                $Prefix = Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.connection.prefix
                $Suffix = Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.connection.suffix

                "$Prefix$Value$Suffix"
            }
        }
        "Parameter" {  }
        "Parm" {
            if ($Prefix -or $Suffix) {
                "$Prefix$Value$Suffix"
            }
            else {
                $Prefix = Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.parm.prefix
                $Suffix = Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.parm.suffix

                "$Prefix$Value$Suffix"
            }
        }
        "Trigger" {
            if ($Prefix -or $Suffix) {
                "$Prefix$Value$Suffix"
            }
            else {
                $Prefix = Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.trigger.prefix
                $Suffix = Get-PSFConfigValue -FullName PsLogicAppExtractor.prefixsuffix.trigger.suffix

                "$Prefix$Value$Suffix"
            }
        }
        Default { "$Prefix$Value$Suffix" }
    }
}

<#
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.tag.prefix' -Value "tag_" -Initialize -Description "The default prefix for Tag objects, used as a fallback value for the Format-Name cmdlet."
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.tag.suffix' -Value "" -Initialize -Description "The default suffix for Tag objects, used as a fallback value for the Format-Name cmdlet. Be default an empty string"
 
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.tag.prefix' -Value "tag_" -Initialize -Description "The default prefix for Tag objects, used as a fallback value for the Format-Name cmdlet."
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.tag.suffix' -Value "" -Initialize -Description "The default suffix for Tag objects, used as a fallback value for the Format-Name cmdlet. Be default an empty string."
 
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.parm.prefix' -Value "parm_" -Initialize -Description "The default prefix for parm (parameter) objects, used as a fallback value for the Format-Name cmdlet."
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.parm.suffix' -Value "" -Initialize -Description "The default suffix for parm (parameter) objects, used as a fallback value for the Format-Name cmdlet. Be default an empty string."
 
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.connection.prefix' -Value "connection_" -Initialize -Description "The default prefix for connection objects, used as a fallback value for the Format-Name cmdlet."
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.connection.suffix' -Value "_id" -Initialize -Description "The default suffix for connection objects, used as a fallback value for the Format-Name cmdlet."
 
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.trigger.prefix' -Value "trigger_" -Initialize -Description "The default prefix for trigger objects, used as a fallback value for the Format-Name cmdlet."
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.trigger.suffix' -Value "" -Initialize -Description "The default suffix for trigger objects, used as a fallback value for the Format-Name cmdlet. Be default an empty string."
 
#>


<#
.SYNOPSIS
Get action from the object, filtered by the type of the action
 
.DESCRIPTION
Get actions and all nested actions, filtered by type
 
.PARAMETER InputObject
The object that you want to work against
 
Will by analyzed to see if it has nested actions, and will be recursively traversed to fetch all actions
 
.PARAMETER Type
The action type that will be outputted
 
.EXAMPLE
PS C:\> Get-ActionsByType -InputObject $obj -Type "Http"
 
Will traverse the $obj and filter actions to only output the ones of the type Http
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Get-ActionsByType {
    param (
        [PsCustomObject] $InputObject,

        [string] $Type
    )
    
    if ($InputObject.Type -eq $Type -or $InputObject.Value.Type -eq $Type) {
        $InputObject
    }

    if ($InputObject.Value.actions) {
        foreach ($item in $InputObject.Value.actions.PsObject.Properties) {
            Get-ActionsByType -InputObject $item -Type $Type
        }
    }
    elseif ($InputObject.actions) {
        foreach ($item in $InputObject.actions.PsObject.Properties) {
            Get-ActionsByType -InputObject $item -Type $Type
        }
    }
}

<#
.SYNOPSIS
Get the value from an ARM template parameter
 
.DESCRIPTION
Gets the current default value from the specified ARM template parameter
 
.PARAMETER InputObject
The ARM object that you want to work against
 
It has to be a object of the type [ArmTemplate] for it to work properly
 
.PARAMETER Name
Name of the parameter that you want to work against
 
.EXAMPLE
PS C:\> Get-ArmParameterValue -InputObject $armObj -Name "logicAppName"
 
Gets the default value from the ARM template parameter: logicAppName
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Get-ArmParameterValue {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object] $InputObject,

        [Alias('ParameterName')]
        [Parameter(Mandatory = $true)]
        [string] $Name
    )
    
    if ($InputObject.parameters.$Name) {
        $InputObject.parameters.$Name.defaultValue
    }
}

<#
.SYNOPSIS
Get the output file
 
.DESCRIPTION
Get the full path of the "latest" file from the workpath of the runbook / extraction process
 
.PARAMETER Path
Path to the workpath where the runbook has been persisting files
 
.EXAMPLE
PS C:\> Get-ExtractOutput -Path "C:\temp\work_directory"
 
Returns the full path of the latest written file from the "C:\temp\work_directory" path
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Get-ExtractOutput {
    [CmdletBinding()]
    param (

        [Alias('WorkPath')]
        [Parameter(Mandatory = $true)]
        [string] $Path
    )
    
    $files = Get-ChildItem -Path $Path -Recurse -File
    $files | Sort-Object -Property LastWriteTime | Select-Object -Last 1 -ExpandProperty FullName
}

<#
.SYNOPSIS
Get parameters from ARM template
 
.DESCRIPTION
Get parameters from the ARM template
 
You can include / exclude parameters, so your parameter file only contains the parameters you want to handle at deployment
 
The default value is promoted as the initial value of the parameter
 
.PARAMETER Path
Path to the ARM template that you want to work against
 
.PARAMETER Exclude
Instruct the cmdlet to exclude the given set of parameter names
 
Supports array / list
 
.PARAMETER Include
Instruct the cmdlet to include the given set of parameter names
 
Supports array / list
 
.PARAMETER AsFile
Instruct the cmdlet to save a valid ARM template parameter file next to the ARM template file
 
.PARAMETER BlankValues
Instructs the cmdlet to blank the values in the parameter file
 
.PARAMETER CopyMetadata
Instructs the cmdlet to copy over the metadata property from the original parameter in the ARM template, if present
 
.EXAMPLE
PS C:\> Get-PsLaArmParameter -Path "C:\temp\work_directory\TestLogicApp.json"
 
Gets all parameters from the "TestLogicApp.json" ARM template
The output is written to the console
 
.EXAMPLE
PS C:\> Get-PsLaArmParameter -Path "C:\temp\work_directory\TestLogicApp.json" -Exclude "logicAppLocation","trigger_Frequency"
 
Gets all parameters from the "TestLogicApp.json" ARM template
Will exclude the parameters "logicAppLocation" & "trigger_Frequency" if present
The output is written to the console
 
.EXAMPLE
PS C:\> Get-PsLaArmParameter -Path "C:\temp\work_directory\TestLogicApp.json" -Include "trigger_Interval","trigger_Frequency"
 
Gets all parameters from the "TestLogicApp.json" ARM template
Will only copy over the parameters "trigger_Interval" & "trigger_Frequency" if present
The output is written to the console
 
.EXAMPLE
PS C:\> Get-PsLaArmParameter -Path "C:\temp\work_directory\TestLogicApp.json" -AsFile
 
Gets all parameters from the "TestLogicApp.json" ARM template
The output is written the "C:\temp\work_directory\TestLogicApp.parameters.json" file
 
.EXAMPLE
PS C:\> Get-PsLaArmParameter -Path "C:\temp\work_directory\TestLogicApp.json" -BlankValues
 
Gets all parameters from the "TestLogicApp.json" ARM template
Blank all values for each parameter
The output is written to the console
 
.EXAMPLE
PS C:\> Get-PsLaArmParameter -Path "C:\temp\work_directory\TestLogicApp.json" -CopyMetadata
 
Gets all parameters from the "TestLogicApp.json" ARM template
Copies over the metadata property from the original parameter, if present
The output is written to the console
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Get-PsLaArmParameter {
    [CmdletBinding()]
    param (
        [string] $Path,

        [string[]] $Exclude,

        [string[]] $Include,

        [switch] $AsFile,

        [switch] $BlankValues,

        [switch] $CopyMetadata
    )

    $armObj = [ArmTemplate]$(Get-TaskWorkObject -Path $Path)

    $res = [ordered]@{}
    $res.'$schema' = "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#"
    $res.contentVersion = "1.0.0.0"
    $res.parameters = [ordered]@{}
      
    foreach ($item in $armObj.parameters.PsObject.Properties) {
        if ($item.Name -in $Exclude) { continue }
        
        if ($Include.Count -gt 0) {
            if (-not ($item.Name -in $Include)) { continue }
        }
        
        $valueObj = [ordered]@{}

        if ($BlankValues) {
            switch ($item.Value.Type) {
                "int" {
                    $valueObj.value = 0
                }
                "bool" {
                    $valueObj.value = $false
                }
                "object" {
                    $valueObj.value = $null
                }
                "array" {
                    $valueObj.value = @()
                }
                Default {
                    $valueObj.value = ""
                }
            }
        }
        else {
            $valueObj.value = $item.Value.DefaultValue
        }

        if ($CopyMetadata -and $item.Value.metadata) {
            $valueObj.metadata = $item.Value.metadata
        }

        $res.parameters."$($item.Name)" = $valueObj
    }

    if ($AsFile) {
        $pathLocal = $Path.Replace(".json", ".parameters.json")

        $encoding = New-Object System.Text.UTF8Encoding($true)
        [System.IO.File]::WriteAllLines($pathLocal, $($([PSCustomObject] $res) | ConvertTo-Json -Depth 10), $encoding)

        Get-Item -Path $pathLocal | Select-Object -ExpandProperty FullName
    }
    else {
        $([PSCustomObject] $res) | ConvertTo-Json -Depth 10
    }
}

<#
.SYNOPSIS
Get tasks that are part of the module
 
.DESCRIPTION
List all avaiable tasks that are part of the module, to be used for exporting, sanitizing and converting a LogicApp into a deployable ARM template
 
.PARAMETER Category
Used to filter the number of tasks down to only being part of the category that you are looking for
 
.PARAMETER Detailed
Instruct the cmdlet to output the details about the tasks in a more detailed fashion, makes it easier to read the descriptions for each task
 
.EXAMPLE
PS C:\> Get-PsLaTask
 
List all available tasks
 
Output example:
 
Category Name Description
-------- ---- -----------
Arm Set-Arm.Connections.ManagedApis.AsParameter Loops all $connections childs…
Arm Set-Arm.Connections.ManagedApis.AsVariable Loops all $connections childs…
Arm Set-Arm.Connections.ManagedApis.IdFormatted Loops all $connections childs…
Arm Set-Arm.IntegrationAccount.IdFormatted.Simple.AsParameter.AsVariable Creates an Arm variable: integrationAccount…
 
.EXAMPLE
PS C:\> Get-PsLaTask -Category Converter
 
List all available tasks, which are in the Converter category
 
Output example:
 
Category Name Description
-------- ---- -----------
Converter ConvertTo-Arm Converts the LogicApp json structure into a valid ARM template json
Converter ConvertTo-Raw Converts the raw LogicApp json structure into the a valid LogicApp json,…
 
.EXAMPLE
PS C:\> Get-PsLaTask -Detailed
 
List all available tasks, and outputs it in the detailed view
 
Output example:
 
Category : Arm
Name : Set-Arm.Connections.ManagedApis.AsParameter
Description : Loops all $connections childs
              -Creates an Arm parameter, with prefix & suffix
              --Sets the default value to the original name, extracted from connectionId property
              -Sets the connectionId to: [resourceId('Microsoft.Web/connections', parameters('XYZ'))]
              -Sets the connectionName to: [parameters('XYZ')]
 
Category : Arm
Name : Set-Arm.Connections.ManagedApis.AsVariable
Description : Loops all $connections childs
              -Creates an Arm variable, with prefix & suffix
              --Sets the value to the original name, extracted from connectionId property
              -Sets the connectionId to: [resourceId('Microsoft.Web/connections', variables('XYZ'))]
              -Sets the connectionName to: [variables('XYZ')]
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Get-PsLaTask {
    [CmdletBinding()]
    param (
        [ValidateSet('Arm', 'Converter', 'Exporter', 'Raw')]
        [string] $Category,

        [switch] $Detailed
    )

    $res = Get-PsLaTaskByPath -Path "$($MyInvocation.MyCommand.Module.ModuleBase)\internal\tasks" | Where-Object Category -like "*$Category*" | Select-Object -Property * -ExcludeProperty Path

    if ($Detailed) {
        $res | Format-List
    }
    else {
        $res
    }
}

<#
.SYNOPSIS
Get tasks that are references from a file
 
.DESCRIPTION
Get tasks that are references from a runbook file, to make it easier to understand what a given runbook file is doing
 
.PARAMETER File
Path to the runbook file, that you want to analyze
 
.PARAMETER Category
Used to filter the number of tasks down to only being part of the category that you are looking for
 
.PARAMETER Detailed
Instruct the cmdlet to output the details about the tasks in a more detailed fashion, makes it easier to read the descriptions for each task
 
.EXAMPLE
PS C:\> Get-PsLaTaskByFile -File "C:\temp\LogicApp.ExportOnly.psakefile.ps1"
 
List all tasks that are referenced from the file
The file needs to be a valid PSake runbook file
 
Output example:
 
Category Name Description
-------- ---- -----------
Converter ConvertTo-Arm Converts the LogicApp json structure into a valid ARM template json
Converter ConvertTo-Raw Converts the raw LogicApp json structure into the a valid LogicApp json,…
Exporter Export-LogicApp.AzCli Exports the raw version of the Logic App from the Azure Portal
 
.EXAMPLE
PS C:\> Get-PsLaTaskByFile -File "C:\temp\LogicApp.ExportOnly.psakefile.ps1" -Category Converter
 
List all tasks that are referenced from the file, which are in the Converter category
The file needs to be a valid PSake runbook file
 
Output example:
 
Category Name Description
-------- ---- -----------
Converter ConvertTo-Arm Converts the LogicApp json structure into a valid ARM template json
Converter ConvertTo-Raw Converts the raw LogicApp json structure into the a valid LogicApp json,…
 
.EXAMPLE
PS C:\> Get-PsLaTaskByFile -File "C:\temp\LogicApp.ExportOnly.psakefile.ps1" -Detailed
 
List all tasks that are referenced from the file, and outputs it in the detailed view
The file needs to be a valid PSake runbook file
 
Output example:
 
Category : Converter
Name : ConvertTo-Arm
Description : Converts the LogicApp json structure into a valid ARM template json
 
Category : Converter
Name : ConvertTo-Raw
Description : Converts the raw LogicApp json structure into the a valid LogicApp json,
              this will remove different properties that are not needed
 
Category : Exporter
Name : Export-LogicApp.AzCli
Description : Exports the raw version of the Logic App from the Azure Portal
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Get-PsLaTaskByFile {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '')]
    [CmdletBinding()]
    param (
        [Alias('Runbook')]
        [Parameter(Mandatory = $true)]
        [string] $File,

        [ValidateSet('Arm', 'Converter', 'Exporter', 'LogicApp')]
        [string] $Category,

        [switch] $Detailed
    )
    
    # We are playing around with the internal / global psake object
    $psake.context = New-Object System.Collections.Stack

    $res = @(Get-PSakeScriptTasks -Runbook $File | Where-Object Name -ne "Default" | Select-Object -Property @{Label = "Category"; Expression = { $_.Alias.Split(".")[0] } }, Name, Description)

    $temp = $res | Where-Object Category -like "*$Category*" | Sort-Object Category, Name
        
    if ($Detailed) {
        $temp | Format-List
    }
    else {
        $temp
    }
}

<#
.SYNOPSIS
Get tasks based on files from a directory
 
.DESCRIPTION
Get tasks from individual files, that are located in a directory
 
.PARAMETER Path
Path to the directory where there are valid PSake tasks saved as ps1 files
 
.EXAMPLE
PS C:\> Get-PsLaTaskByPath -Path c:\temp\tasks
 
List all available tasks, based on the files in the directory
All files has to be valid PSake files saved as ps1 files
 
Output example:
 
Category : Arm
Name : Set-Arm.Connections.ManagedApis.AsParameter
Description : Loops all $connections childs
              -Creates an Arm parameter, with prefix & suffix
              --Sets the default value to the original name, extracted from connectionId property
              -Sets the connectionId to: [resourceId('Microsoft.Web/connections', parameters('XYZ'))]
              -Sets the connectionName to: [parameters('XYZ')]
Path : c:\temp\tasks\Set-Arm.Connections.ManagedApis.AsParameter.task.ps1
File : Set-Arm.Connections.ManagedApis.AsParameter.task.ps1
 
Category : Arm
Name : Set-Arm.Connections.ManagedApis.AsVariable
Description : Loops all $connections childs
              -Creates an Arm variable, with prefix & suffix
              --Sets the value to the original name, extracted from connectionId property
              -Sets the connectionId to: [resourceId('Microsoft.Web/connections', variables('XYZ'))]
              -Sets the connectionName to: [variables('XYZ')]
Path : c:\temp\tasks\Set-Arm.Connections.ManagedApis.AsVariable.task.ps1
File : Set-Arm.Connections.ManagedApis.AsVariable.task.ps1
 
.NOTES
General notes
#>

function Get-PsLaTaskByPath {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string] $Path
    )
    
    $files = Get-ChildItem -Path "$Path\*.ps1"

    $res = New-Object System.Collections.Generic.List[System.Object]

    # We are playing around with the internal / global psake object
    $psake.context = New-Object System.Collections.Stack
    $psake.context.push(
        @{
            "tasks"   = @{}
            "aliases" = @{}
        }
    )

    foreach ($item in $files) {
        # We are playing around with the internal / global psake object
        $psake.context = New-Object System.Collections.Stack
        $psake.context.push(
            @{
                "tasks"   = @{}
                "aliases" = @{}
            }
        )
        
        . $item.FullName

        foreach ($task in $psake.context.tasks) {
            foreach ($value in $task.Values) {
                $res.Add([PsCustomObject][ordered]@{
                        Category    = $value.Alias.Split(".")[0]
                        Name        = $value.Name
                        Description = $value.Description
                        Path        = $item.FullName
                        File        = $item.Name
                    })
            }
        }
    }

    # We are playing around with the internal / global psake object
    $psake.context = New-Object System.Collections.Stack

    $res.ToArray() | Where-Object Name -ne "Default" | Sort-Object Category, Name
}

<#
.SYNOPSIS
Get tasks that are references from a file, with the execution order
 
.DESCRIPTION
Get tasks that are references from a runbook file, to make it easier to understand what a given runbook file is doing
 
Includes the execution order of the tasks, to visualize the tasks sequence
 
.PARAMETER File
Path to the runbook file, that you want to analyze
 
.PARAMETER Detailed
Instruct the cmdlet to output the details about the tasks in a more detailed fashion, makes it easier to read the descriptions for each task
 
.EXAMPLE
PS C:\> Get-PsLaTaskOrderByFile -File "C:\temp\LogicApp.ExportOnly.psakefile.ps1"
 
List all tasks that are referenced from the file
The file needs to be a valid PSake runbook file
 
Output example:
 
ExecutionOrder Category Name Description
-------------- -------- ---- -----------
             1 Exporter Export-LogicApp.AzCli Exports the raw version of the Logic App from the Azure Portal
             2 Converter ConvertTo-Raw Converts the raw LogicApp json structure into the a valid LogicApp j…
             3 Converter ConvertTo-Arm Converts the LogicApp json structure into a valid ARM template json
 
.EXAMPLE
PS C:\> Get-PsLaTaskOrderByFile -File "C:\temp\LogicApp.ExportOnly.psakefile.ps1" -Detailed
 
List all tasks that are referenced from the file, and outputs it in the detailed view
The file needs to be a valid PSake runbook file
 
Output example:
 
ExecutionOrder : 1
Category : Exporter
Name : Export-LogicApp.AzCli
Description : Exports the raw version of the Logic App from the Azure Portal
 
ExecutionOrder : 2
Category : Converter
Name : ConvertTo-Raw
Description : Converts the raw LogicApp json structure into the a valid LogicApp json,
                 this will remove different properties that are not needed
 
ExecutionOrder : 3
Category : Converter
Name : ConvertTo-Arm
Description : Converts the LogicApp json structure into a valid ARM template json
 
.NOTES
General notes
#>

function Get-PsLaTaskOrderByFile {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '')]
    [CmdletBinding()]
    param (
        [Alias('Runbook')]
        [Parameter(Mandatory = $true)]
        [string] $File,

        [switch] $Detailed
    )
    
    # We are playing around with the internal / global psake object
    $psake.context = New-Object System.Collections.Stack

    $default = Get-PSakeScriptTasks -Runbook $File | Where-Object Name -eq "Default" | Select-Object -First 1

    $tasks = @(Get-PSakeScriptTasks -Runbook $File | Where-Object Name -ne "Default" | Select-Object -Property @{Label = "Category"; Expression = { $_.Alias.Split(".")[0] } }, Name, Description)

    $res = @(for ($i = 0; $i -lt $default.DependsOn.Count; $i++) {
            $tasks | Where-Object Name -eq $($default.DependsOn[$i]) | Select-Object -Property @{Label = "ExecutionOrder"; Expression = { $i + 1 } }, *
        })

    if ($Detailed) {
        $res | Format-List
    }
    else {
        $res
    }
}

<#
.SYNOPSIS
Short description
 
.DESCRIPTION
Long description
 
.PARAMETER Category
Instruct the cmdlet which template type you want to have outputted
 
.PARAMETER OutputPath
Path to were the Task template file will be persisted
 
The path has to be a directory
 
The file will be named: _set-XYA.Template.ps1
 
.EXAMPLE
PS C:\> Get-PsLaTaskTemplate -Category "Arm"
 
Outputs the task template of the type Arm to the console
 
.EXAMPLE
PS C:\> Get-PsLaTaskTemplate -Category "Arm" -OutputPath "C:\temp\work_directory"
 
Outputs the task template of the type Arm
Persists the file into the "C:\temp\work_directory" directory
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Get-PsLaTaskTemplate {
    [CmdletBinding()]
    param (
        [ValidateSet('Arm', 'Converter', 'Raw')]
        [string] $Category,

        [string] $OutputPath
    )

    if ($OutputPath) {
        Copy-Item -Path "$($script:ModuleRoot)\internal\tasks\_Set-$Category.Template.tmp" -Destination "$OutputPath\_Set-$Category.Template.ps1" -PassThru -Force | Select-Object -ExpandProperty FullName
    }
    else {
        Get-Content -Path "$($script:ModuleRoot)\internal\tasks\_Set-$Category.Template.tmp" -Raw
    }
}

<#
.SYNOPSIS
Get the object that the task has to work against
 
.DESCRIPTION
Gets the object from the "previous" task, based on the persisted path and loads it into memory using ConvertFrom-Json
 
.PARAMETER Path
Path to the file that you want the task to work against
 
.EXAMPLE
PS C:\> Get-TaskWorkObject
 
Returns the object that is stored at the location passed in the Path parameter
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Get-TaskWorkObject {
    [CmdletBinding()]
    param (
        [string] $Path = $(Get-PSFConfigValue -FullName PsLogicAppExtractor.Execution.TaskInputNext)

    )

    Get-Content -Path $Path -Raw | ConvertFrom-Json
}

<#
.SYNOPSIS
Get the object that the task has to work against, as a raw string
 
.DESCRIPTION
Gets the object from the "previous" task, based on the persisted path and loads it into memory using Get-Content
 
.PARAMETER Path
Path to the file that you want the task to work against
 
.EXAMPLE
PS C:\> Get-TaskWorkObject
 
Returns the object that is stored at the location passed in the Path parameter
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Get-TaskWorkRaw {
    [CmdletBinding()]
    param (
        [string] $Path = $(Get-PSFConfigValue -FullName PsLogicAppExtractor.Execution.TaskInputNext)
    )

    Get-Content -Path $Path -Raw
}

<#
.SYNOPSIS
Execute the extractor process of the LogicApp
 
.DESCRIPTION
Execute all the tasks that have been defined in the runbook file, and get an ARM template as output
 
Depending on the initial extractor task that you are using, your powershell / az cli session needs to be signed in
 
Your runbook file can contain the tasks available from the module, but also your own custom tasks, that you want to have executed as part of the process
 
.PARAMETER Runbook
Path to the PSake valid runbook file that you want to have executed while exporting, sanitizing and converting a LogicApp into a deployable ARM template
 
.PARAMETER SubscriptionId
Id of the subscription that you want to work against, your current powershell / az cli session either needs to "connected" to the subscription or at least have permissions to work against the subscription
 
.PARAMETER ResourceGroup
Name of the resource group that you want to work against, your current powershell / az cli session needs to have permissions to work against the resource group
 
.PARAMETER Name
Name of the logic app, that you want to work against
 
.PARAMETER Task
List of task that you want to have executed, based on the runbook file that you pass
 
This allows you to only run a small subset of all the tasks that you have defined inside your runbook
 
Helpful when troubleshooting and trying to identify the best execution order of all the tasks
 
.PARAMETER WorkPath
Path to were the tasks will persist their outputs
 
Each task will save a file into a unique folder, containing the formatted output from its operation
 
You could risk that secrets or credentials are being stored on your disk, if they in some way are stored as clear text inside the logic app
 
The default valus is the current users TempPath, where it creates a "\PsLogicAppExtractor\GUID\" directory for each invoke
 
.PARAMETER OutputPath
Path to were the ARM template file will be persisted
 
The path has to be a directory
 
The file will be named as the Logic App is named
 
.PARAMETER KeepFiles
Instruct the cmdlet to keep all the files, across all tasks
 
This enables troubleshooting and comparison of input vs output, per task, as each task has an input file and the result of the work persisted in the same directory
 
.EXAMPLE
PS C:\> Invoke-PsLaExtractor -Runbook "C:\temp\LogicApp.ExportOnly.psakefile.ps1" -ResourceGroup "TestRg" -Name TestLogicApp
 
Invokes the different tasks inside the runbook file, to export the TestLogicApp as an ARM template
The file needs to be a valid PSake runbook file
 
.EXAMPLE
PS C:\> Invoke-PsLaExtractor -Runbook "C:\temp\LogicApp.ExportOnly.psakefile.ps1" -SubscriptionId "f5608f3d-ab28-49d8-9b4e-1b1f812d12e0" -ResourceGroup "TestRg" -Name "TestLogicApp"
 
Invokes the different tasks inside the runbook file, to export the TestLogicApp as an ARM template
The file needs to be a valid PSake runbook file
 
.EXAMPLE
PS C:\> Invoke-PsLaExtractor -Runbook "C:\temp\LogicApp.ExportOnly.psakefile.ps1"
 
Invokes the different tasks inside the runbook file, to export the TestLogicApp as an ARM template
The file needs to be a valid PSake runbook file
The runbook file needs to have populated the Properties object, with the minimum: ResourceGroup and SubscriptionId
 
.EXAMPLE
PS C:\> Invoke-PsLaExtractor -Runbook "C:\temp\LogicApp.ExportOnly.psakefile.ps1" -ResourceGroup "TestRg" -Name TestLogicApp -WorkPath "C:\temp\work_directory"
 
Invokes the different tasks inside the runbook file, to export the TestLogicApp as an ARM template
The file needs to be a valid PSake runbook file
Will output all tasks files into the "C:\temp\work_directory" location
 
.EXAMPLE
PS C:\> Invoke-PsLaExtractor -Runbook "C:\temp\LogicApp.ExportOnly.psakefile.ps1" -ResourceGroup "TestRg" -Name TestLogicApp -KeepFiles
 
Invokes the different tasks inside the runbook file, to export the TestLogicApp as an ARM template
The file needs to be a valid PSake runbook file
All files that the different tasks has created, are keept, for the user to analyze them
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Invoke-PsLaExtractor {
    [CmdletBinding(DefaultParameterSetName = "NameOnly")]
    param (
        [Alias('File')]
        [Parameter(Mandatory = $true, ParameterSetName = "NameOnly")]
        [Parameter(Mandatory = $true, ParameterSetName = "PreppedFile")]
        [Parameter(Mandatory = $true, ParameterSetName = "ResourceGroup")]
        [Parameter(Mandatory = $true, ParameterSetName = "Subscription")]
        [string] $Runbook,

        [Parameter(Mandatory = $true, ParameterSetName = "Subscription")]
        [string] $SubscriptionId,

        [Parameter(Mandatory = $true, ParameterSetName = "ResourceGroup")]
        [Parameter(Mandatory = $true, ParameterSetName = "Subscription")]
        [string] $ResourceGroup,

        [Parameter(Mandatory = $true, ParameterSetName = "NameOnly")]
        [Parameter(Mandatory = $true, ParameterSetName = "ResourceGroup")]
        [Parameter(Mandatory = $true, ParameterSetName = "Subscription")]
        [string] $Name,

        [string[]] $Task,

        [string] $WorkPath = "$([System.IO.Path]::GetTempPath())PsLogicAppExtractor\$([System.Guid]::NewGuid().Guid)",

        [string] $OutputPath,

        [switch] $KeepFiles
    )

    if (-not ($WorkPath -like "*$([System.IO.Path]::GetTempPath())*")) {
        if ($WorkPath -NotMatch '(?im)[{(]?[0-9A-F]{8}[-]?(?:[0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?') {
            $WorkPath = "$WorkPath\$([System.Guid]::NewGuid().Guid)"
        }
    }

    #The task counter needs to be reset prior running
    Set-PSFConfig -FullName PsLogicAppExtractor.Execution.TaskCounter -Value 0

    Set-PSFConfig -FullName PsLogicAppExtractor.Execution.TaskInputNext -Value ""
    Set-PSFConfig -FullName PsLogicAppExtractor.Execution.TaskOutputFile -Value ""
    Set-PSFConfig -FullName PsLogicAppExtractor.Execution.TaskPath -Value ""

    #Make sure the work path is created and available
    New-Item -Path $WorkPath -ItemType Directory -Force -ErrorAction Ignore > $null

    $parms = @{}
    $parms.buildFile = $Runbook
    $parms.nologo = $true

    if ($Task) {
        $parms.taskList = $Task
    }
    
    $props = @{}
    if ($SubscriptionId) { $props.SubscriptionId = $SubscriptionId }
    if ($ResourceGroup) { $props.ResourceGroup = $ResourceGroup }
    if ($Name) {
        $props.Name = $Name
    }

    Set-PSFConfig -FullName PsLogicAppExtractor.Execution.WorkPath -Value $WorkPath

    $res = Invoke-psake @parms -properties $props -ErrorVariable errorsFound
    
    if ($errorsFound) {
        throw $res
    }

    $resPath = Get-ExtractOutput -Path $WorkPath

    if ($OutputPath) {
        $resPath = Copy-Item -Path $resPath -Destination "$OutputPath" -PassThru -Force | Select-Object -ExpandProperty FullName
    }

    $resPath

    if (-not $KeepFiles) {
        Get-ChildItem -Path $WorkPath -File -Recurse | Where-Object { $_.FullName -ne $resPath } | ForEach-Object { Remove-Item -Path $_.FullName -Force -ErrorAction SilentlyContinue -Confirm:$false -Recurse }
        Get-ChildItem -Path $WorkPath -Directory | Where-Object { $_.FullName -ne $(Split-Path -Path $resPath -Parent) } | ForEach-Object { Remove-Item -Path $_.FullName -Force -ErrorAction SilentlyContinue -Confirm:$false -Recurse }
    }
}

<#
.SYNOPSIS
Create valid runbook file based on the task files in the directory
 
.DESCRIPTION
Helps you build a valid runbook file, based on all the individual task files (ps1) that are located in the directory
 
This makes it easy to get a starting point for a new runbook file, based on the tasks you have persisted as individual files
 
Especially helpful for custom tasks that might be stored in a central repository
 
The task files has to valid PSake tasks saved as ps1 files
 
.PARAMETER Path
Path to the directory where there are valid PSake tasks saved as ps1 files
 
The default value is set for the internal directory where all the generic tasks that are part of the module is located
 
.PARAMETER SubscriptionId
Id of the subscription that you want to work against
 
At runtime / execution of Invoke-PsLaExtractor - your current powershell / az cli session either needs to "connected" to the subscription or at least have permissions to work against the subscription
 
Useful when you know upfront what you want to work against, as you don't need to pass the parameter into the Invoke-PsLaExtractor
 
.PARAMETER ResourceGroup
Name of the resource group that you want to work against
 
At runtime / execution of Invoke-PsLaExtractor - your current powershell / az cli session needs to have permissions to work against the resource group
 
Useful when you know upfront what you want to work against, as you don't need to pass the parameter into the Invoke-PsLaExtractor
 
.PARAMETER Name
Name of the logic app, that you want to work against
 
At runtime / execution of Invoke-PsLaExtractor - your current powershell / az cli session needs to have permissions to work against the logic app
 
Useful when you know upfront what you want to work against, as you don't need to pass the parameter into the Invoke-PsLaExtractor
 
.PARAMETER OutputPath
Path to were the runbook file will be persisted
 
The path has to be a directory
 
The runbook file will be named: PsLaExtractor.default.psakefile.ps1
 
.PARAMETER IncludePrefixSuffix
Instruct the cmdlet to add the different prefix and suffix options, with the default values that comes with the module
 
This make it easier to make the runbook file work across different environments, without having to worry about prepping different prefix and suffix value prior
 
.EXAMPLE
PS C:\> New-PsLaRunbookByPath
 
Creates a valid runbook file, based on the bare minimum and with sane default values
Reads all the internal / generic tasks that are part of the module and implements a valid default path for the includes
 
.EXAMPLE
PS C:\> New-PsLaRunbookByPath -Path c:\temp\tasks
 
Creates a valid runbook file, based on the bare minimum and with sane default values
Reads all ps1 files located in c:\temp\tasks
 
Great to use when you have lots of custom tasks in a directory / repository, and want a good runbook file as starting point
 
.EXAMPLE
PS C:\> New-PsLaRunbookByPath -SubscriptionId "f5608f3d-ab28-49d8-9b4e-1b1f812d12e0" -ResourceGroup "TestRg"
 
Creates a valid runbook file, based on the bare minimum and with sane default values
Reads all the internal / generic tasks that are part of the module and implements a valid default path for the includes
Prepares the Properties object with SubscriptionId and ResourceGroup
 
Useful if you have multiple logic apps in the same resource group and you want them extracted using the same runbook file
 
.EXAMPLE
PS C:\> New-PsLaRunbookByPath -SubscriptionId "f5608f3d-ab28-49d8-9b4e-1b1f812d12e0" -ResourceGroup "TestRg" -Name "TestLogicApp"
 
Creates a valid runbook file, based on the bare minimum and with sane default values
Reads all the internal / generic tasks that are part of the module and implements a valid default path for the includes
Prepares the Properties object with SubscriptionId and ResourceGroup and Name
 
Useful if you want to have a ready to run runbook file, that makes it simple to run the command again and again
Great for iterative work, where you make lots of small changes in the logic app and want to see how the changes affect your ARM template
 
.EXAMPLE
PS C:\> New-PsLaRunbookByPath -OutputPath c:\temp\PsLaRunbooks
 
Creates a valid runbook file, based on the bare minimum and with sane default values
Reads all the internal / generic tasks that are part of the module and implements a valid default path for the includes
Outputs the build to c:\temp\PsLaRunbooks
 
The runbook file is default named: PsLaExtractor.default.psakefile.ps1
 
.EXAMPLE
PS C:\> New-PsLaRunbookByPath -IncludePrefixSuffix
 
Creates a valid runbook file, based on the bare minimum and with sane default values
Reads all the internal / generic tasks that are part of the module and implements a valid default path for the includes
The Properties object inside the runbook file, will be pre-populated with the default prefix and suffix values from the module
 
This make it easier to make the runbook file work across different environments, without having to worry about prepping different prefix and suffix value prior
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function New-PsLaRunbookByPath {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding()]
    param (
        [string] $Path = $(Get-PSFConfigValue -FullName PsLogicAppExtractor.ModulePath.Tasks),

        [string] $SubscriptionId,

        [string] $ResourceGroup,

        [string] $Name,

        [string] $OutputPath,

        [switch] $IncludePrefixSuffix
    )

    $files = Get-ChildItem -Path "$Path\*.ps1"

    $res = New-Object System.Collections.Generic.List[System.Object]

    $res.AddRange($(Get-BuildHeader -SubscriptionId $SubscriptionId -ResourceGroup $ResourceGroup -Name $Name -IncludePrefixSuffix:$IncludePrefixSuffix))
    
    if ($Path -ne $(Get-PSFConfigValue -FullName PsLogicAppExtractor.ModulePath.Tasks)) {
        $res.Add("# Path to where the custom task files are located")
        $res.Add("`$pathTasksCustom = `"$Path`"")
    }
    
    $res.Add("# Array to hold all tasks for the default task")
    $res.Add("`$listTasks = @()")

    $res.Add("")

    $includes = New-Object System.Collections.Generic.List[System.Object]
    $list = New-Object System.Collections.Generic.List[System.Object]

    $psake.context = New-Object System.Collections.Stack
    $psake.context.push(
        @{
            "tasks"   = @{}
            "aliases" = @{}
        }
    )

    foreach ($item in $files) {

        $psake.context = New-Object System.Collections.Stack
        $psake.context.push(
            @{
                "tasks"   = @{}
                "aliases" = @{}
            }
        )
        
        . $item.FullName

        foreach ($task in $psake.context.tasks) {
            foreach ($value in $task.Values) {
                $list.Add("`$listTasks += `"$($value.Name)`"")
            }
        }
    }

    $psake.context = New-Object System.Collections.Stack

    #TODO! Make sure that we actually need this - other places it was enough with the $psake.context = New-Object System.Collections.Stack
    # $psake.context.push(
    # @{
    # "buildSetupScriptBlock" = {}
    # "buildTearDownScriptBlock" = {}
    # "taskSetupScriptBlock" = {}
    # "taskTearDownScriptBlock" = {}
    # "executedTasks" = new-object System.Collections.Stack
    # "callStack" = new-object System.Collections.Stack
    # "originalEnvPath" = $env:PATH
    # "originalDirectory" = get-location
    # "originalErrorActionPreference" = $global:ErrorActionPreference
    # "tasks" = @{}
    # "aliases" = @{}
    # "properties" = new-object System.Collections.Stack
    # "includes" = new-object System.Collections.Queue
    # }
    # )
    
    $res.Add("# All tasks that needs to be include based on their path")
    $res.AddRange($includes)
    $res.Add("")
    $res.Add("# Building the list of tasks for the default task")
    $res.AddRange($list)
    $res.Add("")
    $res.Add("# Default tasks, the via the dependencies will run all tasks")
    $res.Add("Task -Name `"default`" -Depends `$listTasks")
 
    if ($OutputPath) {
        New-Item -Path $OutputPath -ItemType Directory -Force -ErrorAction Ignore > $null

        $path = Join-Path -Path $OutputPath -ChildPath "PsLaExtractor.default.psakefile.ps1"

        $encoding = New-Object System.Text.UTF8Encoding($true)
        [System.IO.File]::WriteAllLines($path, $($res.ToArray() -join "`r`n"), $encoding)

        Get-Item -Path $path | Select-Object -ExpandProperty FullName
    }
    else {
        $res.ToArray() -join "`r`n"
    }
}

<#
.SYNOPSIS
Create valid runbook file based on the task passed as inputs
 
.DESCRIPTION
Helps you build a valid runbook file, based on all the individual tasks that are passed as inputs
 
This makes it easy to get a starting point for a new runbook file, based on the tasks you have build in your array and then passes into the cmdlet
 
Tasks are expected to be the ones that are part of the module
 
.PARAMETER Task
Names of the tasks that you want to be part of your runbook file
 
Supports array of task names
 
Names of the different tasks are expected to be the ones that are part of the module
 
.PARAMETER SubscriptionId
Id of the subscription that you want to work against
 
At runtime / execution of Invoke-PsLaExtractor - your current powershell / az cli session either needs to "connected" to the subscription or at least have permissions to work against the subscription
 
Useful when you know upfront what you want to work against, as you don't need to pass the parameter into the Invoke-PsLaExtractor
 
.PARAMETER ResourceGroup
Name of the resource group that you want to work against
 
At runtime / execution of Invoke-PsLaExtractor - your current powershell / az cli session needs to have permissions to work against the resource group
 
Useful when you know upfront what you want to work against, as you don't need to pass the parameter into the Invoke-PsLaExtractor
 
.PARAMETER Name
Name of the logic app, that you want to work against
 
At runtime / execution of Invoke-PsLaExtractor - your current powershell / az cli session needs to have permissions to work against the logic app
 
Useful when you know upfront what you want to work against, as you don't need to pass the parameter into the Invoke-PsLaExtractor
 
.PARAMETER OutputPath
Path to were the runbook file will be persisted
 
The path has to be a directory
 
The runbook file will be named: PsLaExtractor.default.psakefile.ps1
 
.PARAMETER IncludePrefixSuffix
Instruct the cmdlet to add the different prefix and suffix options, with the default values that comes with the module
 
This make it easier to make the runbook file work across different environments, without having to worry about prepping different prefix and suffix value prior
 
.EXAMPLE
PS C:\> New-PsLaRunbookByTask -Task "Export-LogicApp.AzCli","ConvertTo-Raw","ConvertTo-Arm"
 
Creates a valid runbook file, based on the bare minimum and with sane default values
Will write the include and taskList in the mentioned order of the tasks
 
.EXAMPLE
PS C:\> New-PsLaRunbookByTask -Task "Export-LogicApp.AzCli","ConvertTo-Raw","ConvertTo-Arm" -SubscriptionId "f5608f3d-ab28-49d8-9b4e-1b1f812d12e0" -ResourceGroup "TestRg"
 
Creates a valid runbook file, based on the bare minimum and with sane default values
Will write the include and taskList in the mentioned order of the tasks
Prepares the Properties object with SubscriptionId and ResourceGroup
 
Useful if you have multiple logic apps in the same resource group and you want them extracted using the same runbook file
 
.EXAMPLE
PS C:\> New-PsLaRunbookByTask -Task "Export-LogicApp.AzCli","ConvertTo-Raw","ConvertTo-Arm" -SubscriptionId "f5608f3d-ab28-49d8-9b4e-1b1f812d12e0" -ResourceGroup "TestRg" -Name "TestLogicApp"
 
Creates a valid runbook file, based on the bare minimum and with sane default values
Will write the include and taskList in the mentioned order of the tasks
Prepares the Properties object with SubscriptionId and ResourceGroup and Name
 
Useful if you want to have a ready to run runbook file, that makes it simple to run the command again and again
Great for iterative work, where you make lots of small changes in the logic app and want to see how the changes affect your ARM template
 
.EXAMPLE
PS C:\> New-PsLaRunbookByTask -Task "Export-LogicApp.AzCli","ConvertTo-Raw","ConvertTo-Arm" -OutputPath c:\temp\PsLaRunbooks
 
Creates a valid runbook file, based on the bare minimum and with sane default values
Will write the include and taskList in the mentioned order of the tasks
Outputs the build to c:\temp\PsLaRunbooks
 
The runbook file is default named: PsLaExtractor.default.psakefile.ps1
 
.EXAMPLE
PS C:\> New-PsLaRunbookByTask -Task "Export-LogicApp.AzCli","ConvertTo-Raw","ConvertTo-Arm" -IncludePrefixSuffix
 
Creates a valid runbook file, based on the bare minimum and with sane default values
Will write the include and taskList in the mentioned order of the tasks
The Properties object inside the runbook file, will be pre-populated with the default prefix and suffix values from the module
 
This make it easier to make the runbook file work across different environments, without having to worry about prepping different prefix and suffix value prior
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function New-PsLaRunbookByTask {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string[]] $Task,
        
        [string] $SubscriptionId,

        [string] $ResourceGroup,

        [string] $Name,

        [string] $OutputPath,

        [switch] $IncludePrefixSuffix
    )
    
    $res = New-Object System.Collections.Generic.List[System.Object]

    $res.AddRange($(Get-BuildHeader -SubscriptionId $SubscriptionId -ResourceGroup $ResourceGroup -Name $Name -IncludePrefixSuffix:$IncludePrefixSuffix))

    $res.Add("# Array to hold all tasks for the default task")
    $res.Add('$listTasks = @()')

    $res.Add('')

    $list = New-Object System.Collections.Generic.List[System.Object]
    
    foreach ($item in $Task) {
        $list.Add("`$listTasks += `"$item`"")
    }

    $res.AddRange($list)
    $res.Add("")
    $res.Add("# Default tasks, the via the dependencies will run all tasks")
    $res.Add('Task -Name "default" -Depends $listTasks')
    
    if ($OutputPath) {
        New-Item -Path $OutputPath -ItemType Directory -Force -ErrorAction Ignore > $null

        $path = Join-Path -Path $OutputPath -ChildPath "PsLaExtractor.default.psakefile.ps1"

        $encoding = New-Object System.Text.UTF8Encoding($true)
        [System.IO.File]::WriteAllLines($path, $($res.ToArray() -join "`r`n"), $encoding)

        Get-Item -Path $path | Select-Object -ExpandProperty FullName
    }
    else {
        $res.ToArray() -join "`r`n"
    }
}

<#
.SYNOPSIS
Output the tasks result to a file
 
.DESCRIPTION
Persists the tasks output into a file, either the raw content or the object
 
Sets the $Script:FilePath = $Path, to ensure the next tasks can pick up the file and continue its work
 
.PARAMETER Path
Path to where the tasks wants the ouput to be persisted
 
.PARAMETER Content
Raw string that should be written to the desired path
 
.PARAMETER InputObject
The object that should be written to the desired path
 
Will be converted to a json string, usign the ConvertTo-Json
 
Important note: If you need the InputObject to be written with a specific structure, the object has to be of the expected type before being passed into the cmdlet
A simple cast can ensure this to work as intended
 
.EXAMPLE
PS C:\> Out-TaskFile -Path "C:\temp\work_directory\1_Export-LogicApp.AzCli" -InputObject $([ArmTemplate]$armObj)
 
Outputs the armObj variable to the path: "C:\temp\work_directory\1_Export-LogicApp.AzCli"
The armObj is casted to the [ArmTemplate] type, to ensure it is persisted as the expected json structure
 
.EXAMPLE
PS C:\> Out-TaskFile -Path "C:\temp\work_directory\1_Export-LogicApp.AzCli" -Content '{"Test":"Test"}'
     
Outputs the content string: '{"Test":"Test"}' to the path: "C:\temp\work_directory\1_Export-LogicApp.AzCli"
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Out-TaskFile {
    [CmdletBinding(DefaultParameterSetName = "InputObject")]
    param (
        [string] $Path = $(Get-PSFConfigValue -FullName PsLogicAppExtractor.Execution.TaskOutputFile),

        [Parameter(Mandatory = $true, ParameterSetName = "Content")]
        [string] $Content,

        [Parameter(Mandatory = $true, ParameterSetName = "InputObject")]
        [object] $InputObject
    )
    
    if ($InputObject) {
        $Content = $InputObject | ConvertTo-Json -Depth 20
    }

    $encoding = New-Object System.Text.UTF8Encoding($true)
    [System.IO.File]::WriteAllLines($Path, $Content, $encoding)
    
    if ($(Get-PSFConfigValue -FullName PsLogicAppExtractor.Execution.TaskInputNext)) {
        $taskPath = Get-PSFConfigValue -FullName PsLogicAppExtractor.Execution.TaskPath
        Copy-Item -Path $(Get-PSFConfigValue -FullName PsLogicAppExtractor.Execution.TaskInputNext) -Destination "$taskPath\Input.json"
    }

    Set-PSFConfig -FullName PsLogicAppExtractor.Execution.TaskInputNext -Value $Path
}

<#
.SYNOPSIS
Output the tasks result to a file, as an ARM template
 
.DESCRIPTION
Persists the tasks output into a file, as an ARM template
 
.PARAMETER InputObject
The object that should be written to the desired path
 
Will be converted to a json string, usign the ConvertTo-Json
 
.EXAMPLE
PS C:\> Out-TaskFileArm -InputObject $armObj
 
Outputs the armObj variable
The armObj is casted to the [ArmTemplate] type, to ensure it is persisted as the expected json structure
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Out-TaskFileArm {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object] $InputObject
    )

    Out-TaskFile -InputObject $([ArmTemplate]$InputObject)
}

<#
.SYNOPSIS
Output the tasks result to a file, as a LogicApp json structure
 
.DESCRIPTION
Persists the tasks output into a file, as a LogicApp json structure
 
.PARAMETER InputObject
The object that should be written to the desired path
 
Will be converted to a json string, usign the ConvertTo-Json
 
.EXAMPLE
PS C:\> Out-TaskFileLogicApp -InputObject $lgObj
 
Outputs the armObj variable
The armObj is casted to the [ArmTemplate] type, to ensure it is persisted as the expected json structure
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Out-TaskFileLogicApp {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object] $InputObject
    )

    Out-TaskFile -InputObject $([LogicApp]$InputObject)
}

<#
.SYNOPSIS
Remove parameter from the ARM template
 
.DESCRIPTION
Removes an ARM template parameter by the name provided
 
.PARAMETER InputObject
The ARM object that you want to work against
 
It has to be a object of the type [ArmTemplate] for it to work properly
 
.PARAMETER Name
Name of the parameter that you want to work against
 
If the parameter exists, it will be removed from the InputObject
 
.EXAMPLE
PS C:\> Remove-ArmParameter -InputObject $armObj -Name "logicAppName"
 
Removes the logicAppName ARM template parameter
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Remove-ArmParameter {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object] $InputObject,

        [Alias('ParameterName')]
        [Parameter(Mandatory = $true)]
        [string] $Name
    )

    if ($InputObject.parameters.$Name) {
        $InputObject.parameters.PsObject.Properties.Remove($Name)
    }

    $InputObject
}

<#
.SYNOPSIS
Remove variable from the ARM template
 
.DESCRIPTION
Removes an ARM template variable by the name provided
 
.PARAMETER InputObject
The ARM object that you want to work against
 
It has to be a object of the type [ArmTemplate] for it to work properly
 
.PARAMETER Name
Name of the variable that you want to work against
 
If the variable exists, it will be removed from the InputObject
 
.EXAMPLE
PS C:\> Remove-ArmVariable -InputObject $armObj -Name "logicAppName"
 
Removes the logicAppName ARM template variable
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Remove-ArmVariable {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object] $InputObject,

        [Alias('VariableName')]
        [Parameter(Mandatory = $true)]
        [string] $Name
    )

    if ($InputObject.variables.$Name) {
        $InputObject.variables.PsObject.Properties.Remove($Name)
    }

    $InputObject
}

<#
.SYNOPSIS
Remove parm (parameter) from the LogicApp
 
.DESCRIPTION
Removes an LogicApp parm (parameter) by the name provided
 
.PARAMETER InputObject
The LogicApp object that you want to work against
 
It has to be a object of the type [LogicApp] for it to work properly
 
.PARAMETER Name
Name of the parm (parameter) that you want to work against
 
If the parm (parameter) exists, it will be removed from the InputObject
 
.EXAMPLE
PS C:\> Remove-LogicAppParm -InputObject $armObj -Name "TriggerQueue"
 
Removes the TriggerQueue LogicApp parm (parameter)
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Remove-LogicAppParm {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [object] $InputObject,

        [Alias('ParmName')]
        [Parameter(Mandatory = $true)]
        [string] $Name
    )
    
    if ($InputObject.properties.definition.parameters.$Name) {
        $InputObject.properties.definition.parameters.PsObject.Properties.Remove($Name)
    }

    $InputObject
}

<#
.SYNOPSIS
Set the current working directory
 
.DESCRIPTION
Sets the current tasks working directory, based on the current PsLaWorkPath and the execution number that task is in the overall execution
 
Outputs the path that has been constructed
 
.PARAMETER Path
Path to the current working directory
 
The value passed in should always be the $PsLaWorkPath, to ensure that things are working
 
.PARAMETER FileName
The of the file that you want to be configured for the task
 
Is normally equal to the name of the Logic App
 
.EXAMPLE
PS C:\> Set-TaskWorkDirectory
 
Creates a new sub directory under the $PsLaWorkPath location
The sub directory is named "$taskCounter`_$TaskName"
 
The output will be: "$Path\$taskCounter`_$TaskName"
 
.NOTES
 
Author: Mötz Jensen (@Splaxi)
 
#>

function Set-TaskWorkDirectory {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
    [CmdletBinding()]
    param (
        [string] $Path = $(Get-PSFConfigValue -FullName PsLogicAppExtractor.Execution.WorkPath),
        
        [string] $FileName = "$Name.json"
    )
    Set-PSFConfig -FullName PsLogicAppExtractor.Execution.TaskCounter -Value $($(Get-PSFConfigValue -FullName PsLogicAppExtractor.Execution.TaskCounter) + 1)
    $taskCounter = $(Get-PSFConfigValue -FullName PsLogicAppExtractor.Execution.TaskCounter)
    
    $taskName = $($psake.context.Peek().CurrentTaskName)
    $newPath = "$Path\$taskCounter`_$TaskName"
    New-Item -Path $newPath -ItemType Directory -Force -ErrorAction Ignore > $null

    Set-PSFConfig -FullName PsLogicAppExtractor.Execution.TaskPath -Value $newPath
    Set-PSFConfig -FullName PsLogicAppExtractor.Execution.TaskOutputFile -Value "$newPath\$fileName"
}

<#
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 'PsLogicAppExtractor' -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 'PsLogicAppExtractor' -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 'PsLogicAppExtractor' -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 'PsLogicAppExtractor' -Name 'ModulePath.Base' -Value $script:ModuleRoot -Initialize -Description "The base path for the module, used for the internal functions to be able to provide the full path for internal objects."
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'ModulePath.Tasks' -Value "$($script:ModuleRoot)\internal\tasks" -Initialize -Description "The full path for all generic tasks that are part of the module."
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'ModulePath.Classes' -Value "$($script:ModuleRoot)\internal\Classes" -Initialize -Description "The full path for all the generic classes that are part of the module."

Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.tag.prefix' -Value "tag_" -Initialize -Description "The default prefix for Tag objects, used as a fallback value for the Format-Name cmdlet."
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.tag.suffix' -Value "" -Initialize -Description "The default suffix for Tag objects, used as a fallback value for the Format-Name cmdlet. Be default an empty string"

Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.parm.prefix' -Value "parm_" -Initialize -Description "The default prefix for parm (parameter) objects, used as a fallback value for the Format-Name cmdlet."
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.parm.suffix' -Value "" -Initialize -Description "The default suffix for parm (parameter) objects, used as a fallback value for the Format-Name cmdlet. Be default an empty string."

Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.connection.prefix' -Value "connection_" -Initialize -Description "The default prefix for connection objects, used as a fallback value for the Format-Name cmdlet."
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.connection.suffix' -Value "_id" -Initialize -Description "The default suffix for connection objects, used as a fallback value for the Format-Name cmdlet."

Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.trigger.prefix' -Value "trigger_" -Initialize -Description "The default prefix for trigger objects, used as a fallback value for the Format-Name cmdlet."
Set-PSFConfig -Module 'PsLogicAppExtractor' -Name 'prefixsuffix.trigger.suffix' -Value "" -Initialize -Description "The default suffix for trigger objects, used as a fallback value for the Format-Name cmdlet. Be default an empty string."


<#
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 'PsLogicAppExtractor.ScriptBlockName' -Scriptblock {
     
}
#>


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


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


New-PSFLicense -Product 'PsLogicAppExtractor' -Manufacturer 'Motz' -ProductVersion $script:ModuleVersion -ProductType Module -Name MIT -Version "1.0.0.0" -Date (Get-Date "2022-04-04") -Text @"
Copyright (c) 2022 Motz
 
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"@

#endregion Load compiled code