PSDSC.psm1

#Region '.\prefix.ps1' -1

$Script:IsPowerShellCore = $PSVersionTable.PSEdition -eq 'Core'

if ($Script:IsPowerShellCore -and $IsWindows)
{
    Import-Module -Name 'PSDesiredStateConfiguration' -MinimumVersion 2.0.7 -Prefix 'Pwsh' -ErrorAction SilentlyContinue
}
#EndRegion '.\prefix.ps1' 7
#Region '.\Classes\10.ManifestFile.ps1' -1

class ResourceManifest
{
    [System.String] $type
    [System.String] $description
    [System.Version] $version
    [System.Array] $resourceInput

    ResourceManifest ()
    {
    }

    ResourceManifest ([string] $type, [string] $description, [version] $version)
    {
        $this.type = $type
        $this.description = $description
        $this.version = $version
    }

    ResourceManifest ([string] $type, [string] $description, [version] $version, [array] $resourceInput)
    {
        $this.type = $type
        $this.description = $description
        $this.version = $version
        $this.resourceInput = $resourceInput
    }
}
#EndRegion '.\Classes\10.ManifestFile.ps1' 27
#Region '.\Classes\DscConfigCompleters.ps1' -1

class DscConfigCompleter : System.Management.Automation.IArgumentCompleter
{
    [System.Collections.Generic.IEnumerable[System.Management.Automation.CompletionResult]] CompleteArgument(
        [string] $CommandName,
        [string] $ParameterName,
        [string] $wordToComplete,
        [System.Management.Automation.Language.CommandAst] $CommandAst,
        [Collections.IDictionary] $fakeBoundParameters
    )
    {
        $exe = ResolveDscExe -ErrorAction SilentlyContinue

        $list = [System.Collections.Generic.List[System.Management.Automation.CompletionResult]]::new()

        if ($exe)
        {
            $resources = GetDscResourceDetail -Exclude @{kind = 'Group' } # don't include Group resources
            foreach ($resource in $resources)
            {
                $CompletionText = $resource
                $ListItemText = $resource
                $ResultType = [System.Management.Automation.CompletionResultType]::ParameterValue
                $ToolTip = $resource

                $obj = [System.Management.Automation.CompletionResult]::new($CompletionText, $ListItemText, $ResultType, $Tooltip)
                $list.add($obj)
            }

            return $list
        }
        else
        {
            return $list
        }
    }
}

class DscConfigInputCompleter : System.Management.Automation.IArgumentCompleter
{
    [System.Collections.Generic.IEnumerable[System.Management.Automation.CompletionResult]] CompleteArgument(
        [string] $CommandName,
        [string] $ParameterName,
        [string] $wordToComplete,
        [System.Management.Automation.Language.CommandAst] $CommandAst,
        [Collections.IDictionary] $fakeBoundParameters
    )
    {
        if ($fakeBoundParameters.ContainsKey('ResourceName'))
        {
            [array]$Resources = GetDscRequiredKey -BuildHashTable | Where-Object {
                $_.type -eq $fakeBoundParameters.ResourceName
            } | Select-Object -ExpandProperty resourceInput -Unique | Sort-Object
        }
        else
        {
            [array]$Resources = @()
        }

        $list = [System.Collections.Generic.List[System.Management.Automation.CompletionResult]]::new()

        foreach ($Resource in $Resources)
        {
            $CompletionText = $Resource
            $ListItemText = $Resource
            $ResultType = [System.Management.Automation.CompletionResultType]::ParameterValue

            $ToolTip = '{0}' -f $fakeBoundParameters.ResourceName

            $obj = [System.Management.Automation.CompletionResult]::new($CompletionText, $ListItemText, $ResultType, $Tooltip)
            $list.add($obj)
        }

        return $list
    }
}
#EndRegion '.\Classes\DscConfigCompleters.ps1' 76
#Region '.\Classes\DscResourceCompleters.ps1' -1

class DscResourceCompleter : System.Management.Automation.IArgumentCompleter
{
    [System.Collections.Generic.IEnumerable[System.Management.Automation.CompletionResult]] CompleteArgument(
        [string] $CommandName,
        [string] $ParameterName,
        [string] $wordToComplete,
        [System.Management.Automation.Language.CommandAst] $CommandAst,
        [Collections.IDictionary] $fakeBoundParameters
    )
    {
        $exe = ResolveDscExe -ErrorAction SilentlyContinue

        $list = [System.Collections.Generic.List[System.Management.Automation.CompletionResult]]::new()

        if ($exe)
        {
            $manifestFiles = Get-ChildItem -Path (Split-Path $exe -Parent) -Depth 1 -Filter "*.dsc.resource.json"
            foreach ($manifest in $manifestFiles)
            {
                $typeName = (Get-Content $manifest | ConvertFrom-Json -ErrorAction SilentlyContinue).type

                $CompletionText = $typeName
                $ListItemText = $typeName
                $ResultType = [System.Management.Automation.CompletionResultType]::ParameterValue
                $ToolTip = $typeName

                $obj = [System.Management.Automation.CompletionResult]::new($CompletionText, $ListItemText, $ResultType, $Tooltip)
                $list.add($obj)
            }

            # section to include PSTypes data
            $psTypes = ReadDscPsAdapterSchema -ReturnTypeInfo
            $psTypes | ForEach-Object { $list.Add($_) }
            return $list
        }
        else
        {
            return $list
        }
    }
}

class DscResourceInputCompleter : System.Management.Automation.IArgumentCompleter
{
    [System.Collections.Generic.IEnumerable[System.Management.Automation.CompletionResult]] CompleteArgument(
        [string] $CommandName,
        [string] $ParameterName,
        [string] $wordToComplete,
        [System.Management.Automation.Language.CommandAst] $CommandAst,
        [Collections.IDictionary] $fakeBoundParameters
    )
    {
        if ($fakeBoundParameters.ContainsKey('ResourceName'))
        {
            [array]$Resources = GetDscRequiredKey | Where-Object {
                $_.type -eq $fakeBoundParameters.ResourceName
            } | Select-Object -ExpandProperty resourceInput -Unique | Sort-Object
        }
        else
        {
            [array]$Resources = @()
        }

        $list = [System.Collections.Generic.List[System.Management.Automation.CompletionResult]]::new()

        foreach ($Resource in $Resources)
        {
            $CompletionText = "'$Resource'"
            $ListItemText = "'$Resource'"
            $ResultType = [System.Management.Automation.CompletionResultType]::ParameterValue

            $ToolTip = '{0}' -f $fakeBoundParameters.ResourceName

            $obj = [System.Management.Automation.CompletionResult]::new($CompletionText, $ListItemText, $ResultType, $Tooltip)
            $list.add($obj)
        }

        return $list

    }
}
#EndRegion '.\Classes\DscResourceCompleters.ps1' 82
#Region '.\Classes\DscVersionCompleters.ps1' -1

class DscVersionCompleter : System.Management.Automation.IArgumentCompleter
{
    [System.Collections.Generic.IEnumerable[System.Management.Automation.CompletionResult]] CompleteArgument(
        [string] $CommandName,
        [string] $ParameterName,
        [string] $wordToComplete,
        [System.Management.Automation.Language.CommandAst] $CommandAst,
        [Collections.IDictionary] $fakeBoundParameters
    )
    {
        [array]$DscVersions = (GetDscVersion -UseGitHub) | Where-Object { $_ -like "$wordToComplete*" }

        $list = [System.Collections.Generic.List[System.Management.Automation.CompletionResult]]::new()

        foreach ($DscVersion in $DscVersions)
        {
            $CompletionText = $DscVersion
            $ListItemText = $DscVersion
            $ResultType = [System.Management.Automation.CompletionResultType]::ParameterValue
            $ToolTip = $DscVersion

            $obj = [System.Management.Automation.CompletionResult]::new($CompletionText, $ListItemText, $ResultType, $Tooltip)
            $list.add($obj)
        }

        return $list

    }
}
#EndRegion '.\Classes\DscVersionCompleters.ps1' 30
#Region '.\Private\DSC\BuildDscConfigDocument.ps1' -1

function BuildDscConfigDocument
{
    <#
    .SYNOPSIS
        Build DSC configuration document
 
    .DESCRIPTION
        The function BuildDscConfigDocument builds a Desired State Configuration version 3 document.
 
    .PARAMETER Path
        The path to a valid Configuration Document.
 
    .PARAMETER Content
        The content to a valid DSC Configuration Document.
 
    .EXAMPLE
        PS C:\> $path = 'myConfig.ps1'
        PS C:\> BuildDscConfigDocument -Path $path
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Path')]
    [OutputType([System.Collections.Specialized.OrderedDictionary])]
    param
    (
        [Parameter(Mandatory = $true,
            ParameterSetName = 'Path')]
        [ValidateScript({
                if (-Not ($_ | Test-Path) )
                {
                    throw "File or folder does not exist"
                }
                if (-Not ($_ | Test-Path -PathType Leaf) )
                {
                    throw "The Path argument must be a file. Folder paths are not allowed."
                }
                return $true
            })]
        [System.String]
        $Path,

        [Parameter(Mandatory = $true,
            ParameterSetName = 'Content')]
        [System.String]
        $Content
    )

    # create configuration document resource class (can be re-used)
    $configurationDocument = [PSCustomObject]@{
        name       = $null
        type       = $null
        properties = @{}
    }

    # convert object
    $rs = ConvertToDscObject @PSBoundParameters -ErrorAction SilentlyContinue

    $configurationDocument.name = ($rs | Select-Object -First 1 -ExpandProperty ConfigurationName)
    $configurationDocument.type = ($rs | Select-Object -First 1 -ExpandProperty Type)

    # bag to hold resources
    $resourceProps = [System.Collections.Generic.List[object]]::new()

    foreach ($resource in $rs)
    {
        # props
        $properties = @{}

        # TODO: make the dependsOn key using resourceId() function
        $resource.GetEnumerator() | ForEach-Object { if ($_.Key -notin @('ResourceInstanceName', 'ResourceName', 'ModuleName', 'DependsOn', 'ConfigurationName', 'Type'))
            {
                $properties.Add($_.Key, $_.Value)
            } }

        # build the module
        $inputObject = [PSCustomObject]@{
            name       = $resource.ResourceInstanceName
            type       = ("{0}/{1}" -f $resource.ModuleName, $resource.ResourceName)
            properties = $properties
        }

        # add to bag
        $resourceProps.Add($inputObject)
    }

    # add all the resources
    $configurationDocument.properties = @{
        resources = $resourceProps
    }

    # TODO: get schema information from GitHub
    $configurationDocument = [ordered]@{
        "`$schema" = "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json"
        resources  = @($configurationDocument)
    }

    return $configurationDocument
}
#EndRegion '.\Private\DSC\BuildDscConfigDocument.ps1' 100
#Region '.\Private\DSC\BuildDscInput.ps1' -1

function BuildDscInput
{
    <#
    .SYNOPSIS
        Build the Desired State Configuration input string.
 
    .DESCRIPTION
        The function BuildDscInput builds the argument input string to pass to 'dsc.exe'.
 
    .PARAMETER Command
        The sub command to run e.g. config
 
    .PARAMETER Operation
        The operation to run e.g. Set
 
    .PARAMETER ResourceName
        The resource name to execute.
 
    .PARAMETER ResourceInput
        The resource input to provide. Supports JSON, YAML path and PowerShell hash table.
 
    .PARAMETER Parameter
        Optionally, the parameter input to provide.
 
    .EXAMPLE
        PS C:\> BuildDscInput -Arguments config -Operation get -ResourceInput myconfig.dsc.config.yaml
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateSet("config", "resource")]
        [System.String]
        $Command,

        [Parameter(Mandatory = $true)]
        [ValidateSet('list', 'get', 'set', 'test', 'delete', 'export')]
        [System.String]
        $Operation,

        [Parameter(Mandatory = $false)]
        [Alias('Name')]
        [System.String]
        $ResourceName,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [object]
        $ResourceInput,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [object]
        $Parameter
    )

    # string to build
    $sb = [System.Text.StringBuilder]::new($Command)

    if ($Command -eq 'config' -and $Operation -in @('list', 'delete'))
    {
        # TODO: helpful documentation to use _exist for set operation
        Throw "Operation not valid when running 'dsc config'. Please use different combination."
    }

    switch ($Command)
    {
        'config'
        {
            # start validating parameters first
            $paramValue = ConfirmDscInput -Command $Command -ParameterInput $Parameter

            if ($paramValue)
            {
                Write-Debug -Message "Appending '$ParamValue'"
                $sb.Append(" $paramValue") | Out-Null
            }

            $sb.Append(" $Operation") | Out-Null

            $inputValue = ConfirmDscInput -Command $Command -ResourceInput $ResourceInput

            if ($inputValue)
            {
                $sb.Append(" $inputValue") | Out-Null
            }
        }
        'resource'
        {
            # operation comes first
            $sb.Append(" $Operation") | Out-Null

            $inputValue = ConfirmDscInput -Command $Command -ResourceInput $ResourceInput

            if ($Operation -ne 'List' -and ([string]::IsNullOrEmpty($ResourceName)))
            {
                Throw ("You are attempting to run 'dsc resource' using operation '{0}' without resource name. Please specify the resource name using -ResourceName" -f $Operation)
            }

            if ($Operation -eq 'List')
            {
                $inputValue = $null

                $string = " --adapter $ResourceName"
                # TODO: validate the resource name should be an adapter
            }

            if ($Operation -in @('get', 'set', 'test', 'delete', 'export'))
            {
                $string = " --resource $ResourceName"
            }

            $sb.Append($string) | Out-Null

            if ($inputValue)
            {
                $sb.Append(" $inputValue") | Out-Null
            }
        }
        Default
        {
        }
    }

    return $sb.ToString()
}
#EndRegion '.\Private\DSC\BuildDscInput.ps1' 131
#Region '.\Private\DSC\ConfirmDscInput.ps1' -1

function ConfirmDscInput
{
    <#
    .SYNOPSIS
        Confirm input and builds arguments required for 'dsc.exe'
 
    .DESCRIPTION
        The function ConfirmDscInput confirms the input that is passed from higher-level functions. It returns the arguments for building the System.Diagnostics.Process object.
 
    .PARAMETER Command
        The command to run e.g. config
 
    .PARAMETER ResourceInput
        The resource input to provide. Supports JSON, YAML path and PowerShell hash table.
 
    .PARAMETER ParameterInput
        Optionally, the parameter input to provide.
 
    .EXAMPLE
        PS C:\> ConfirmDscInput -Command resource -ResourceInput @{keyPath = 'HKCU\1'}
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding(DefaultParameterSetName = 'ByInput')]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateSet('config', 'resource')]
        [System.String]
        $Command,

        [Parameter(Mandatory = $false, ParameterSetName = 'ByInput')]
        [System.Object]
        [AllowNull()]
        $ResourceInput,

        [Parameter(Mandatory = $false, ParameterSetName = 'ByParameter')]
        [System.Object]
        [AllowNull()]
        $ParameterInput
    )

    $Path = $false

    if ([string]::IsNullOrEmpty($ResourceInput) -and [string]::IsNullOrEmpty($ParameterInput))
    {
        return
    }

    if ($PSCmdlet.ParameterSetName -eq 'ByInput' -and $ResourceInput)
    {
        # check the type of ResourceInput and process accordingly
        if ($ResourceInput -is [string] -and (Test-Path -Path $ResourceInput -ErrorAction SilentlyContinue))
        {
            $extension = (Get-Item $ResourceInput -ErrorAction SilentlyContinue).Extension
            if ($extension -in @('.json', '.yaml', '.yml'))
            {
                Write-Debug -Message "The '$ResourceInput' is a valid path string."
                $out = $ResourceInput

                # set variable
                $Path = $true
            }

            if ($extension -eq '.ps1' -and $Command -eq 'config')
            {
                Write-Debug -Message "The '$ResourceInput' is a PowerShell (.ps1) script. Converting..."
                $out = ConvertTo-DscJson -Path $ResourceInput

                Write-Debug -Message "The converted JSON is:"
                Write-Debug -Message $out
            }
        }
        elseif ($ResourceInput -is [hashtable])
        {
            # resourceInput is a hashtable
            $json = $ResourceInput | ConvertTo-Json -Depth 10 -Compress
            $out = $json
        }
        elseif ($ResourceInput -is [string])
        {
            try
            {
                $json = $ResourceInput | ConvertFrom-Json
                $out = $json | ConvertTo-Json -Depth 10 -Compress
            }
            catch
            {
                Write-Debug -Message "The '$ResourceInput' is not a valid JSON string. Please make sure the input is valid JSON."
            }
        }
        else
        {
            # TODO: check if YAML can be used to convert with ConvertFrom-Yaml
        }
    }


    # process ParameterInput if provided
    if ($PSCmdlet.ParameterSetName -eq 'ByParameter' -and $ParameterInput)
    {
        if ($ParameterInput -is [string] -and (Test-Path -Path $ParameterInput -ErrorAction SilentlyContinue))
        {
            $extension = (Get-Item $ParameterInput -ErrorAction SilentlyContinue).Extension
            if ($extension -in @('.json', '.yaml', '.yml'))
            {
                Write-Debug -Message "The '$ParameterInput' is a valid path string."
                $out = $ParameterInput

                # set variable
                $Path = $true
            }
        }
        elseif ($ParameterInput -is [hashtable])
        {
            $json = $ParameterInput | ConvertTo-Json -Compress
            $out = $json
        }
        elseif ($ParameterInput -is [string])
        {
            try
            {
                $json = $ParameterInput | ConvertFrom-Json
                $out = $json | ConvertTo-Json -Depth 10 -Compress
            }
            catch
            {
                Write-Debug -Message "The '$ParameterInput' is not a valid JSON string. Please make sure the input is valid JSON."
            }
        }
    }

    switch ($Command)
    {
        'config'
        {
            if ($Path -and $PSCmdlet.ParameterSetName -eq 'ByInput')
            {
                $string = "--path $out"
            }
            elseif ($PSCmdlet.ParameterSetName -eq 'ByInput')
            {
                $string = ("--document {0}" -f ($out | ConvertTo-Json) -replace "\\\\", "\")
            }
            elseif ($Path -and $PSCmdlet.ParameterSetName -eq 'ByParameter')
            {
                $string = "--parameters-file $out"
            }
            else
            {
                $string = ("--parameters $(($out | ConvertTo-Json) -replace "\\\\", "\")" -replace "`r`n", "")
            }
        }
        'resource'
        {
            if (-not $extension)
            {
                $string = ("--input {0}" -f ($out | ConvertTo-Json) -replace "\\\\", "\")
            }
            else
            {
                $string = "--path $out"
            }
        }
    }
    return $string
}
#EndRegion '.\Private\DSC\ConfirmDscInput.ps1' 170
#Region '.\Private\DSC\ConvertToDscObject.ps1' -1

function ConvertToDscObject
{
    <#
    .SYNOPSIS
        Convert DSC configuration documents to object
 
    .DESCRIPTION
        The function ConvertToDscObject converts Configuration Document(s) to an hashtable object
 
    .PARAMETER Path
        The path to a valid Configuration Document.
 
    .PARAMETER Content
        The content to a valid DSC Configuration Document.
 
    .EXAMPLE
        PS C:\> $path = 'myConfig.ps1'
        PS C:\> ConvertToDscObject -Path $path
 
    .NOTES
        Credits to: https://github.com/microsoft/DSCParser/tree/master
    #>

    [CmdletBinding(DefaultParameterSetName = 'Path')]
    [OutputType([System.Array])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '', Justification = 'Function converted from Microsoft.')]
    param
    (
        [Parameter(Mandatory = $true,
            ParameterSetName = 'Path')]
        [ValidateScript({
                if (-Not ($_ | Test-Path) )
                {
                    throw "File or folder does not exist"
                }
                if (-Not ($_ | Test-Path -PathType Leaf) )
                {
                    throw "The Path argument must be a file. Folder paths are not allowed."
                }
                if (($_ | Get-Item).Extension -ne '.ps1' )
                {
                    throw "The Path argument must be a file and end with '.ps1'. Folder paths are not allowed."
                }
                return $true
            })]
        [System.String]
        $Path,

        [Parameter(Mandatory = $true,
            ParameterSetName = 'Content')]
        [System.String]
        $Content
    )

    $result = @()
    $Tokens = $null
    $ParseErrors = $null


    # Use the AST to parse the DSC configuration
    if (-not [System.String]::IsNullOrEmpty($Path) -and [System.String]::IsNullOrEmpty($Content))
    {
        if (-not ([System.IO.Path]::GetExtension($Path)))
        {
            Throw "The file must end with .ps1."
        }
        $Content = Get-Content $Path -Raw
    }

    # Remove the module version information.
    $start = $Content.ToLower().IndexOf('import-dscresource')
    if ($start -ge 0)
    {
        $end = $Content.IndexOf("`n", $start)
        if ($end -gt $start)
        {
            $start = $Content.ToLower().IndexOf("-moduleversion", $start)
            if ($start -ge 0 -and $start -lt $end)
            {
                $Content = $Content.Remove($start, $end - $start)
            }
        }
    }

    # Rename the configuration node to ensure a valid name is used.
    $start = $Content.ToLower().IndexOf("`nconfiguration")
    if ($start -lt 0)
    {
        $start = $Content.ToLower().IndexOf(' configuration ')
    }
    if ($start -ge 0)
    {
        $end = $Content.IndexOf("`n", $start)
        if ($end -gt $start)
        {
            $start = $Content.ToLower().IndexOf(' ', $start + 1)
            if ($start -ge 0 -and $start -lt $end)
            {
                $Content = $Content.Remove($start, $end - $start)
                $Content = $Content.Insert($start, " TempDSCParserConfiguration")
            }
        }
    }

    $AST = [System.Management.Automation.Language.Parser]::ParseInput($Content, [ref]$Tokens, [ref]$ParseErrors)

    # Look up the Configuration definition ("")
    $Config = $AST.Find({ $Args[0].GetType().Name -eq 'ConfigurationDefinitionAst' }, $False)

    # Retrieve information about the DSC Modules imported in the config
    # and get the list of their associated resources.
    $ModulesToLoad = @()
    foreach ($statement in $config.body.ScriptBlock.EndBlock.Statements)
    {
        if ($null -ne $statement.CommandElements -and $null -ne $statement.CommandElements[0].Value -and `
                $statement.CommandElements[0].Value -eq 'Import-DSCResource')
        {
            $currentModule = @{}
            for ($i = 0; $i -le $statement.CommandElements.Count; $i++)
            {
                if ($statement.CommandElements[$i].ParameterName -eq 'ModuleName' -and `
                    ($i + 1) -lt $statement.CommandElements.Count)
                {
                    $moduleName = $statement.CommandElements[$i + 1].Value
                    $currentModule.Add('ModuleName', $moduleName)
                }
                elseif ($statement.CommandElements[$i].ParameterName -eq 'ModuleVersion' -and `
                    ($i + 1) -lt $statement.CommandElements.Count)
                {
                    $moduleVersion = $statement.CommandElements[$i + 1].Value
                    $currentModule.Add('ModuleVersion', $moduleVersion)
                }
            }
            $ModulesToLoad += $currentModule
        }
    }
    $DSCResources = @()
    foreach ($moduleToLoad in $ModulesToLoad)
    {
        $loadedModuleTest = Get-Module -Name $moduleToLoad.ModuleName -ListAvailable | Where-Object -FilterScript { $_.Version -eq $moduleToLoad.ModuleVersion }

        if ($null -eq $loadedModuleTest -and -not [System.String]::IsNullOrEmpty($moduleToLoad.ModuleVersion))
        {
            throw "Module {$($moduleToLoad.ModuleName)} version {$($moduleToLoad.ModuleVersion)} specified in the configuration isn't installed on the machine/agent. Install it by running: Install-Module -Name '$($moduleToLoad.ModuleName)' -RequiredVersion '$($moduleToLoad.ModuleVersion)'"
        }
        else
        {
            Write-Verbose -Message ("Retrieving module: {0}" -f $moduleToLoad.ModuleName)
            if ($Script:IsPowerShellCore)
            {
                $currentResources = Get-PwshDscResource -Module $moduleToLoad.ModuleName
            }
            else
            {
                $currentResources = Get-DSCResource -Module $moduleToLoad.ModuleName
            }

            if (-not [System.String]::IsNullOrEmpty($moduleToLoad.ModuleVersion))
            {
                $currentResources = $currentResources | Where-Object -FilterScript { $_.Version -eq $moduleToLoad.ModuleVersion }
            }
            $DSCResources += $currentResources
        }
    }

    # Drill down
    # Body.ScriptBlock is the part after "Configuration <InstanceName> {"
    # EndBlock is the actual code within that Configuration block
    # Find the first DynamicKeywordStatement that has a word "Node" in it, find all "NamedBlockAst" elements, these are the DSC resource definitions
    try
    {
        $resourceInstances = $Config.Body.ScriptBlock.EndBlock.Statements.Find({ $Args[0].GetType().Name -eq 'DynamicKeywordStatementAst' -and $Args[0].CommandElements[0].StringConstantType -eq 'BareWord' -and $Args[0].CommandElements[0].Value -eq 'Node' }, $False).commandElements[2].ScriptBlock.Find({ $Args[0].GetType().Name -eq 'NamedBlockAst' }, $False).Statements
    }
    catch
    {
        $resourceInstances = $Config.Body.ScriptBlock.EndBlock.Statements | Where-Object -FilterScript { $null -ne $_.CommandElements -and $_.CommandElements[0].Value -ne 'Import-DscResource' }
    }

    # Get the name of the configuration.
    $configurationName = $Config.InstanceName.Value

    $totalCount = 1
    foreach ($resource in $resourceInstances)
    {
        $currentResourceInfo = @{}

        # CommandElements
        # 0 - Resource Type
        # 1 - Resource Instance Name
        # 2 - Key/Pair Value list of parameters.
        $resourceType = $resource.CommandElements[0].Value
        $resourceInstanceName = $resource.CommandElements[1].Value

        $percent = ($totalCount / ($resourceInstances.Count) * 100)
        Write-Progress -Status "[$totalCount/$($resourceInstances.Count)] $resourceType - $resourceInstanceName" `
            -PercentComplete $percent `
            -Activity "Parsing Resources"
        $currentResourceInfo.Add("ResourceName", $resourceType)
        $currentResourceInfo.Add("ResourceInstanceName", $resourceInstanceName)
        $currentResourceInfo.Add("ModuleName", $ModulesToLoad.ModuleName)
        $currentResourceInfo.Add("ConfigurationName", $configurationName)
        $adapter = 'Microsoft.DSC/PowerShell'
        if ($PSVersionTable.PSEdition -ne 'Core')
        {
            $adapter = 'Microsoft.Windows/WindowsPowerShell'
        }
        $currentResourceInfo.Add("Type", $adapter)

        # Get a reference to the current resource.
        $currentResource = $DSCResources | Where-Object -FilterScript { $_.Name -eq $resourceType }

        # Loop through all the key/pair value
        foreach ($keyValuePair in $resource.CommandElements[2].KeyValuePairs)
        {
            $isVariable = $false
            $key = $keyValuePair.Item1.Value

            if ($null -ne $keyValuePair.Item2.PipelineElements)
            {
                if ($null -eq $keyValuePair.Item2.PipelineElements.Expression.Value)
                {
                    if ($null -ne $keyValuePair.Item2.PipelineElements.Expression)
                    {
                        if ($keyValuePair.Item2.PipelineElements.Expression.StaticType.Name -eq 'Object[]')
                        {
                            $value = $keyValuePair.Item2.PipelineElements.Expression.SubExpression
                            $newValue = @()
                            foreach ($expression in $value.Statements.PipelineElements.Expression)
                            {
                                if ($null -ne $expression.Elements)
                                {
                                    foreach ($element in $expression.Elements)
                                    {
                                        if ($null -ne $element.VariablePath)
                                        {
                                            $newValue += "`$" + $element.VariablePath.ToString()
                                        }
                                        elseif ($null -ne $element.Value)
                                        {
                                            $newValue += $element.Value
                                        }
                                    }
                                }
                                else
                                {
                                    $newValue += $expression.Value
                                }
                            }
                            $value = $newValue
                        }
                        else
                        {
                            $value = $keyValuePair.Item2.PipelineElements.Expression.ToString()
                        }
                    }
                    else
                    {
                        $value = $keyValuePair.Item2.PipelineElements.Parent.ToString()
                    }

                    if ($value.GetType().Name -eq 'String' -and $value.StartsWith('$'))
                    {
                        $isVariable = $true
                    }
                }
                else
                {
                    $value = $keyValuePair.Item2.PipelineElements.Expression.Value
                }
            }

            # Retrieve the current property's type based on the resource's schema.
            $currentPropertyInResourceSchema = $currentResource.Properties | Where-Object -FilterScript { $_.Name -eq $key }
            $valueType = $currentPropertyInResourceSchema.PropertyType

            # If the value type is null, then the parameter doesn't exist
            # in the resource's schema and we throw a warning
            $propertyFound = $true
            if ($null -eq $valueType)
            {
                $propertyFound = $false
                Write-Warning "Defined property {$key} was not found in resource {$resourceType}"
            }

            if ($propertyFound)
            {
                # If the current property is not a CIMInstance
                if (-not $valueType.StartsWith('[MSFT_') -and `
                        $valueType -ne '[string]' -and `
                        $valueType -ne '[string[]]' -and `
                        -not $isVariable)
                {
                    # Try to parse the value based on the retrieved type.
                    $scriptBlock = @"
                                    `$typeStaticMethods = $valueType | gm -static
                                    if (`$typeStaticMethods.Name.Contains('TryParse'))
                                    {
                                        $valueType::TryParse(`$value, [ref]`$value) | Out-Null
                                    }
"@

                    Invoke-Expression -Command $scriptBlock | Out-Null
                }
                elseif ($valueType -eq '[String]' -or $isVariable)
                {
                    if ($isVariable -and [Boolean]::TryParse($value.TrimStart('$'), [ref][Boolean]))
                    {
                        if ($value -eq "`$true")
                        {
                            $value = $true
                        }
                        else
                        {
                            $value = $false
                        }
                    }
                    else
                    {
                        $value = $value
                    }
                }
                elseif ($valueType -eq '[string[]]')
                {
                    # If the property is an array but there's only one value
                    # specified as a string (not specifying the @()) then
                    # we need to create the array.
                    if ($value.GetType().Name -eq 'String' -and -not $value.StartsWith('@('))
                    {
                        $value = @($value)
                    }
                }
                else
                {
                    $isArray = $false
                    if ($keyValuePair.Item2.ToString().StartsWith('@('))
                    {
                        $isArray = $true
                    }
                    if ($isArray)
                    {
                        $value = @($value)
                    }
                }
                $currentResourceInfo.Add($key, $value) | Out-Null
            }
        }

        $result += $currentResourceInfo
        $totalCount++
    }
    Write-Progress -Completed `
        -Activity "Parsing Resources"

    return [System.Array]$result
}
#EndRegion '.\Private\DSC\ConvertToDscObject.ps1' 354
#Region '.\Private\DSC\ExportDscResourceCommand.ps1' -1

function ExportDscResourceCommand
{
    <#
    .SYNOPSIS
        Run export operation against DSC resource
 
    .DESCRIPTION
        The function FindDscResourceCommand exports a DSC resource / adapter
 
    .PARAMETER ResourceName
        The resource name to export against.
 
    .EXAMPLE
        PS C:\> FindDscResourceCommand -ResourceName Microsoft.VSCode.Dsc/VSCodeExtension
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $ResourceName
    )

    begin
    {
        $commandData = GetDscCommandIndex -CommandName $MyInvocation.MyCommand.Name

        $arguments = BuildDscInput -Command $commandData.Command -Operation $commandData.Operation -ResourceName $ResourceName

        # TODO: we can still make a call to the resource manifest and see if input is required
    }

    process
    {
        # get the System.Diagnostics.Process object
        $process = GetNetProcessObject -Arguments $arguments

        # start the process
        $inputObject = StartNetProcessObject -Process $process
    }

    end
    {
        # return
        $inputObject
    }
}
#EndRegion '.\Private\DSC\ExportDscResourceCommand.ps1' 51
#Region '.\Private\DSC\FindDscResourceCommand.ps1' -1

function FindDscResourceCommand
{
    <#
    .SYNOPSIS
        Run list operation against DSC resource
 
    .DESCRIPTION
        The function FindDscResourceCommand lists a DSC resource / adapter
 
    .PARAMETER ResourceName
        The resource name to get against.
 
    .EXAMPLE
        PS C:\> FindDscResourceCommand -ResourceName Microsoft.PowerShell/DSC
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $ResourceName
    )

    begin
    {
        $commandData = GetDscCommandIndex -CommandName $MyInvocation.MyCommand.Name

        $arguments = BuildDscInput -Command $commandData.Command -Operation $commandData.Operation -ResourceName $ResourceName

        # TODO: we can still make a call to the resource manifest and see if input is required
    }

    process
    {
        # get the System.Diagnostics.Process object
        $process = GetNetProcessObject -Arguments $arguments

        # start the process
        $inputObject = StartNetProcessObject -Process $process
    }

    end
    {
        # return
        $inputObject
    }
}
#EndRegion '.\Private\DSC\FindDscResourceCommand.ps1' 51
#Region '.\Private\DSC\GetDscCommandIndex.ps1' -1

function GetDscCommandIndex
{
    <#
    .SYNOPSIS
        Get command index data.
 
    .DESCRIPTION
        The function GetDscCommandIndex gets the command index data for commands created.
 
    .PARAMETER CommandName
        The command name to return data for.
 
    .PARAMETER IncludeCommand
        Include the sub command as StringBuilder
 
    .EXAMPLE
        PS C:\> GetDscCommandIndex -CommandName GetDscResourceCommand
 
        Returns:
        Name SubCommand
        ---- ----------
        {[SubCommand, resource get]}
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param
    (
        [Parameter(Mandatory = $false)]
        [System.String]
        $CommandName
    )

    # TODO: When version information is available, we can get it using Get-Item and use ResolveDscExe
    # $resolveExe = ResolveDscExe
    if (-not $script:version)
    {
        $versionProc = GetNetProcessObject
        $script:version = ((StartNetProcessObject -Process $versionProc).Output.Trim() -split "-") | Select-Object -Last 1
    }

    # TODO: can add without version later
    $cmdData = @{
        'GetDscResourceCommand'    = @{
            'preview.11' = @{
                SubCommand = @('resource', 'get')
            }
        }
        'SetDscResourceCommand'    = @{
            'preview.11' = @{
                SubCommand = @('resource', 'set')
            }
        }
        'TestDscResourceCommand'   = @{
            'preview.11' = @{
                SubCommand = @('resource', 'test')
            }
        }
        'RemoveDscResourceCommand' = @{
            'preview.11' = @{
                SubCommand = @('resource', 'delete')
            }
        }
        'ExportDscResourceCommand' = @{
            'preview.11' = @{
                SubCommand = @('resource', 'export')
            }
        }
        'FindDscResourceCommand'   = @{
            'preview.11' = @{
                SubCommand = @('resource', 'list')
            }
        }
        'GetDscConfigCommand'      = @{
            'preview.11' = @{
                SubCommand = @('config', 'get')
            }
        }
        'SetDscConfigCommand'      = @{
            'preview.11' = @{
                SubCommand = @('config', 'set')
            }
        }
        'TestDscConfigCommand'     = @{
            'preview.11' = @{
                SubCommand = @('config', 'test')
            }
        }
        'RemoveDscConfigCommand'   = @{
            'preview.11' = @{
                SubCommand = @('config', 'delete')
            }
        }
    }

    $keyData = $cmdData.$CommandName.$Version

    if (-not $keyData)
    {
        Throw "Command '$CommandName' not implemented for version: $version."
    }

    Write-Verbose -Message "Selected data for '$CommandName'"
    return ([PSCustomObject]@{
            Command   = $keyData.SubCommand[0]
            Operation = $keyData.SubCommand[1]
        })
}
#EndRegion '.\Private\DSC\GetDscCommandIndex.ps1' 111
#Region '.\Private\DSC\GetDscConfigCommand.ps1' -1

function GetDscConfigCommand
{
    <#
    .SYNOPSIS
        Run get config against DSC resource
 
    .DESCRIPTION
        The function GetDscResourceCommand gets a DSC config
 
    .PARAMETER ResourceInput
        The resource input to provide. Supports JSON, path and PowerShell scripts.
 
    .PARAMETER Parameter
        Optionally, the parameter input to provide.
 
    .EXAMPLE
         PS C:\> GetDscConfigCommand -ResourceInput myconfig.dsc.config.yaml -Parameter myconfig.dsc.config.parameters.yaml
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [AllowNull()]
        [object]
        $ResourceInput,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [object]
        $Parameter
    )

    begin
    {
        # collect the command from index
        $commandData = GetDscCommandIndex -CommandName $MyInvocation.MyCommand.Name

        # build the input string for arguments
        $arguments = BuildDscInput -Command $commandData.Command -Operation $commandData.Operation -ResourceInput $ResourceInput -Parameter $Parameter
    }

    process
    {
        # get the System.Diagnostics.Process object
        $process = GetNetProcessObject -Arguments $arguments

        # start the process
        $inputObject = StartNetProcessObject -Process $process
    }

    end
    {
        # return
        $inputObject
    }
}
#EndRegion '.\Private\DSC\GetDscConfigCommand.ps1' 60
#Region '.\Private\DSC\GetDscRequiredKey.ps1' -1

function GetDscRequiredKey
{
    <#
    .SYNOPSIS
        Get the required DSC key(s) from Resource Manifest(s).
 
    .DESCRIPTION
        The function GetDscRequiredKey gets all the required keys located in 'dsc.exe' installation location.
        It reads all resource manifest to build the object and captures the resource input if possible.
 
    .PARAMETER Path
        The path to the 'dsc.exe' installation location.
 
    .PARAMETER BuildHashTable
        A switch parameter to indicate if the output should be a hashtable.
 
    .EXAMPLE
        PS C:\> $resolvedPath = Split-Path (Get-Command 'dsc').Source
        PS C:\> GetDscRequiredKey -Path $resolvedPath
 
        Returns ResourceManifest object
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $false)]
        [System.String]
        $Path,

        [Parameter()]
        [System.Management.Automation.SwitchParameter]$BuildHashTable
    )

    if (-not $PSBoundParameters.ContainsKey('Path'))
    {
        $Path = Split-Path (ResolveDscExe) -Parent
    }

    $mFiles = Get-ChildItem -Path $Path -Depth 1 -Filter "*.dsc.resource.json"

    if ($mFiles)
    {
        $p = @{
            ResourceManifest = $mFiles
            BuildHashTable   = $BuildHashTable.IsPresent
        }

        $inputObject = ReadDscRequiredKey @p
    }

    $inputObject
}
#EndRegion '.\Private\DSC\GetDscRequiredKey.ps1' 56
#Region '.\Private\DSC\GetDscResourceCommand.ps1' -1

function GetDscResourceCommand
{
    <#
    .SYNOPSIS
        Run get operation against DSC resource
 
    .DESCRIPTION
        The function GetDscResourceCommand gets a DSC resource
 
    .PARAMETER ResourceName
        The resource name to get against.
 
    .PARAMETER ResourceInput
        The resource input to provide. Supports JSON, path and PowerShell scripts.
 
    .EXAMPLE
        PS C:\> GetDscResourceCommand -ResourceName Microsoft.Windows/Registry -ResourceInput @{keyPath = 'HKCU\Microsoft'}
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $ResourceName,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [object]
        $ResourceInput
    )

    begin
    {
        $commandData = GetDscCommandIndex -CommandName $MyInvocation.MyCommand.Name

        $arguments = BuildDscInput -Command $commandData.Command -Operation $commandData.Operation -ResourceInput $ResourceInput -ResourceName $ResourceName

        # TODO: we can still make a call to the resource manifest and see if input is required
    }

    process
    {
        # get the System.Diagnostics.Process object
        $process = GetNetProcessObject -Arguments $arguments

        # start the process
        $inputObject = StartNetProcessObject -Process $process
    }
    end
    {
        # return
        $inputObject
    }
}
#EndRegion '.\Private\DSC\GetDscResourceCommand.ps1' 58
#Region '.\Private\DSC\GetDscResourceDetail.ps1' -1

function GetDscResourceDetail
{
    <#
    .SYNOPSIS
        Get the resource detail from *.dsc.resource.json file.
 
    .DESCRIPTION
        The function GetDscResourceDetail gets the resource details from the *.dsc.resource.json file(s) and returns the type of the resource.
 
    .PARAMETER Path
        The path that locates 'dsc.exe' file. If not provided, the function will resolve the path to 'dsc.exe'.
 
    .PARAMETER Exclude
        The hashtable that contains the key-value pair to exclude the JSON content. The key is the property name and the value is the property value.
 
        For example: @{kind = 'Group'} excludes all resource(s) that have the kind of 'Group'.
 
    .EXAMPLE
        PS C:\> GetDscResourceDetail
 
        Returns all resources from the *.dsc.resource.json file(s).
 
    .EXAMPLE
        PS C:\> GetDscResourceDetail -Exclude @{kind = 'Adapter'}
 
        Returns all resources from the *.dsc.resource.json file(s) excluding the resources that have the kind of 'Adapter'.
 
    .OUTPUTS
        System.String
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
 
    #>

    [CmdletBinding()]
    [OutputType([System.Collections.Generic.List[System.String]])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPositionalParameters', '', Justification = 'PowerShell 7+.')]
    param (
        [Parameter(Mandatory = $false)]
        [System.String]
        $Path = (ResolveDscExe),

        [Parameter(Mandatory = $false)]
        [System.Collections.Hashtable]
        [AllowNull()]
        $Exclude
    )

    if (-not (Test-Path $Path -ErrorAction SilentlyContinue))
    {
        Throw "Path does not exist: $Path. Please ensure the path is correct to 'dsc.exe'."
    }

    # Get the current directory 'dsc.exe' sits in
    $current = Split-Path $Path -Parent

    # Get all JSON files that match the pattern *.dsc.resource.json
    $jsonFiles = Get-ChildItem -Path $current -Filter '*.dsc.resource.json'

    # Initialize an array to hold the results
    $results = [System.Collections.Generic.List[System.String]]::new()

    foreach ($file in $jsonFiles)
    {
        try
        {
            # Read the content of the JSON file
            $jsonContent = Get-Content -Path $file.FullName -Raw | ConvertFrom-Json

            if (-not ([string]::IsNullOrEmpty($Exclude)))
            {
                if ($jsonContent.$($Exclude.Keys) -eq $Exclude.Values)
                {
                    # Set to null as we are not adding any excludes
                    $jsonContent = $null
                }
            }

            # Filter the JSON content based on type
            if ($null -ne $jsonContent.type -and $jsonContent.kind -ne 'Adapter')
            {
                $results.Add($jsonContent.type)
            }

            if ($jsonContent.kind -eq 'Adapter' -and $jsonContent.type -eq 'Microsoft.DSC/PowerShell')
            {
                if ($IsLinux -or $IsMacOS)
                {
                    $cacheFilePath = Join-Path $env:HOME ".dsc" "PSAdapterCache.json"
                }
                else
                {
                    $cacheFilePath = Join-Path $env:LocalAppData "dsc" "PSAdapterCache.json"
                }

                if (Test-Path $cacheFilePath)
                {
                    try
                    {
                        $cacheContent = Get-Content -Path $cacheFilePath -Raw | ConvertFrom-Json
                        $cacheContent.ResourceCache.Type | ForEach-Object {
                            $results.Add($_)
                        }
                    }
                    catch
                    {
                        Write-Debug "Failed to read or parse cache file: $cacheFilePath. Exception: $_"
                    }
                }
                else
                {
                    Write-Debug -Message ("'PSAdapterCache.json' file not found at: $cacheFilePath. Please run './powershell.resource.ps1 List' to generate the cache file found at '{0}'." -f (Join-Path $current 'psDscAdapter'))
                }
            }

            if ($jsonContent.kind -eq 'Adapter' -and $jsonContent.type -eq 'Microsoft.Windows/WindowsPowerShell')
            {
                if ($PSVersionTable.PSVersion.Major -le 5)
                {
                    Join-Path $env:LocalAppData "dsc\WindowsPSAdapterCache.json"
                }
                else
                {
                    Join-Path $env:HOME ".dsc" "PSAdapterCache.json"
                }

                if (Test-Path $cacheFilePath)
                {
                    $cacheContent = Get-Content -Path $cacheFilePath -Raw | ConvertFrom-Json
                    $cacheContent.ResourceCache.Type | ForEach-Object {
                        $results.Add($_)
                    }
                }
                else
                {
                    Write-Debug -Message ("'WindowsPSAdapterCache' file not found at: $cacheFilePath. Please run './powershell.resource.ps1 List' to generate the cache file found at '{0}'." -f (Join-Path $current 'psDscAdapter'))
                }
            }
        }
        catch
        {
            Write-Debug "Failed to process file: $($file.FullName). Exception: $_"
        }
    }

    # Return the filtered results
    return $results
}
#EndRegion '.\Private\DSC\GetDscResourceDetail.ps1' 149
#Region '.\Private\DSC\GetDscVersion.ps1' -1

function GetDscVersion
{
    <#
    .SYNOPSIS
        Get DSC version available.
 
    .DESCRIPTION
        The function GetDscVersion gets the DSC version available locally or on GitHub.
 
    .PARAMETER UseGitHub
        Switch to search for using GitHub.
 
    .EXAMPLE
        PS C:\> GetDscVersion -UseGitHub
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter()]
        [System.Management.Automation.SwitchParameter]
        $UseGitHub
    )

    if ($UseGitHub.IsPresent)
    {
        if ($null -eq $script:availableDscVersions)
        {
            try
            {
                $script:availableDscVersions = GetGithubReleaseVersion -Organization 'PowerShell' -Repository 'DSC' -ErrorAction Stop
            }
            catch
            {
                $script:availableDscVersions = @()
                Write-Verbose -Message "Failed to retrieve all available versions with error: $_"
            }
        }

        return $script:availableDscVersions
    }

    if (TestDsc)
    {
        # do it like this to avoid circular dependencies because of WinGet
        $process = GetNetProcessObject -Arguments '--version' -ExePath (Get-Command -Name 'dsc.exe').Source
        $version = ((StartNetProcessObject -Process $process).Output -split "-")[-1]
        return $version
    }
    else
    {
        return ""
    }
}
#EndRegion '.\Private\DSC\GetDscVersion.ps1' 58
#Region '.\Private\DSC\ReadDscPsAdapterSchema.ps1' -1

function ReadDscPsAdapterSchema
{
    <#
    .SYNOPSIS
        Read the Desired State Configuration PowerShell adapter cache.
 
    .DESCRIPTION
        The function ReadDscPsAdapterSchema reads the PowerShell adapter cache both for Windows PowerShell and PowerShell.
        It builds upon the work of Andrew Menagarishvili, one of the core members on the DSC project, and reads the required files generated by 'powershell.resource.ps1'.
 
    .PARAMETER ReturnTypeInfo
        Switch parameter to only return the type name(s).
 
    .PARAMETER BuildHashTable
        A switch parameter to indicate if the output should be a hashtable.
 
    .EXAMPLE
        PS C:\> ReadDscAdapterSchema
 
        Returns:
        Type Description Version ResourceInput
        ---- ----------- ------- -------------
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.Array])]
    param
    (
        [System.Management.Automation.SwitchParameter]
        $ReturnTypeInfo,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]
        $BuildHashTable
    )

    begin
    {
        Write-Verbose -Message ("Starting: {0}" -f $MyInvocation.MyCommand.Name)
    }

    process
    {
        $cacheFilePath = if ($IsWindows)
        {
            # PS 6+ on Windows
            Join-Path -Path $env:LocalAppData "dsc\PSAdapterCache.json"
        }
        else
        {
            # either WinPS or PS 6+ on Linux/Mac
            if ($PSVersionTable.PSVersion.Major -le 5)
            {
                Join-Path -Path $env:LocalAppData "dsc\WindowsPSAdapterCache.json"
            }
            else
            {
                Join-Path -Path $env:HOME ".dsc" "PSAdapterCache.json"
            }

        }

        if (-not (Test-Path $cacheFilePath))
        {
            return
        }

        Write-Verbose -Message ("Retrieving cache content from: '{0}'" -f $cacheFilePath)

        $cacheContent = Get-Content $cacheFilePath | ConvertFrom-Json

        $objectBag = foreach ($resource in $cacheContent.ResourceCache)
        {
            # create manifest object
            $resourceObject = [PSCustomObject]@{
                Type          = $resource.type
                Description   = $resource.DscResourceInfo.FriendlyName
                Version       = $resource.DscResourceInfo.Version
                ResourceInput = $null
            }

            # add the example code
            $pParams = @{
                properties     = $resource.DscResourceInfo.Properties
                BuildHashTable = $BuildHashTable.IsPresent
            }
            $exampleCode = @((ReadDscPsAdapterSchemaProperty @pParams))
            $resourceObject.resourceInput = $exampleCode

            $resourceObject
        }
    }
    end
    {
        Write-Verbose -Message ("Ended: {0}" -f $MyInvocation.MyCommand.Name)
        if ($ReturnTypeInfo)
        {
            return ($objectBag.Type)
        }
        return $objectBag
    }
}
#EndRegion '.\Private\DSC\ReadDscPsAdapterSchema.ps1' 105
#Region '.\Private\DSC\ReadDscPsAdapterSchemaProperty.ps1' -1

function ReadDscPsAdapterSchemaProperty
{
    <#
    .SYNOPSIS
        Reads the schema properties of a DSC PowerShell adapter.
 
    .DESCRIPTION
        The function ReadDscPsAdapterSchemaProperty processes the properties of a DSC PowerShell adapter and returns them in a specified format. It can return the properties as a JSON string or as a hash table string.
 
    .PARAMETER Properties
        The properties of the DSC PowerShell adapter to be processed. This parameter is mandatory.
 
    .PARAMETER BuildHashTable
        A switch parameter that indicates whether to build the output as a hash table string. If not specified, the output will be in JSON format.
 
    .OUTPUTS
        System.Collections.Generic.List[System.String]
        Returns a list of strings containing the processed properties in the specified format.
 
    .EXAMPLE
        PS C:\> $properties = @(
            [PSCustomObject]@{ Name = 'Property1'; PropertyType = '[string]'; IsMandatory = $true },
            [PSCustomObject]@{ Name = 'Property2'; PropertyType = '[int]'; IsMandatory = $false }
        )
        PS C:\> ReadDscPsAdapterSchemaProperty -Properties $properties
 
        Returns:
 
        This example processes the given properties and returns them in JSON format.
 
    .EXAMPLE
        PS C:\> $properties = @(
            [PSCustomObject]@{ Name = 'Property1'; PropertyType = '[string]'; IsMandatory = $true },
            [PSCustomObject]@{ Name = 'Property2'; PropertyType = '[int]'; IsMandatory = $false }
        )
        PS C:\> ReadDscPsAdapterSchemaProperty -Properties $properties -BuildHashTable
 
        This example processes the given properties and returns them as a hash table string.
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.Collections.Generic.List[System.String]])]
    param (
        [Parameter(Mandatory = $true)]
        [PSObject] $Properties,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]
        $BuildHashTable
    )

    $resourceInput = [System.Collections.Generic.List[System.String]]::new()
    $inputObject = [ordered]@{}
    $mandatory = [ordered]@{}

    foreach ($property in $Properties)
    {
        $typeName = $property.PropertyType.Split(".")[-1].TrimEnd("]").Replace("[", "")
        $inputObject[$property.Name] = "<$typeName>"

        if ($property.IsMandatory -eq $true)
        {
            $mandatory[$property.Name] = "<$typeName>"
        }
    }

    if ($BuildHashTable.IsPresent)
    {
        $resourceInput.Add((ConvertToHashString -HashTable $mandatory))
        $resourceInput.Add((ConvertToHashString -HashTable $inputObject))
    }
    else
    {
        $resourceInput.Add(($mandatory | ConvertTo-Json -Depth 10 -Compress))
        $resourceInput.Add(($inputObject | ConvertTo-Json -Depth 10 -Compress))
    }

    return $resourceInput
}
#EndRegion '.\Private\DSC\ReadDscPsAdapterSchemaProperty.ps1' 82
#Region '.\Private\DSC\ReadDscRequiredKey.ps1' -1

function ReadDscRequiredKey
{
    <#
    .SYNOPSIS
        Read DSC keys from Resource Manifest.
 
    .DESCRIPTION
        The function ReadDscRequiredKey reads the DSC keys from Resource Manifest.
 
    .PARAMETER ResourceManifest
        The resource manifest to read.
 
    .PARAMETER BuildHashTable
        A switch parameter to indicate if the output should be a hashtable.
 
    .EXAMPLE
        PS C:\> ReadDscRequiredKey -Path "$env:ProgramFiles\DSC\registry.dsc.resource.json"
 
        Returns
        type description version resourceInput
        ---- ----------- ------- -------------
        Microsoft.Windows/Registry Manage Windows Registry keys and values 0.1.0
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.Collections.Generic.List[ResourceManifest]])]
    param
    (
        [Parameter(Mandatory = $false)]
        [System.IO.FileInfo[]]
        $ResourceManifest,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]
        $BuildHashTable
    )

    begin
    {
        Write-Verbose -Message ("Starting: {0}" -f $MyInvocation.MyCommand.Name)
    }

    process
    {
        $objectBag = [System.Collections.Generic.List[ResourceManifest]]::new()

        foreach ($manifest in $ResourceManifest)
        {
            try
            {
                Write-Verbose -Message ("Reading file: '{0}'" -f $manifest.Name)
                $ctx = Get-Content $manifest | ConvertFrom-Json -ErrorAction SilentlyContinue

                # expect to be command-based
                if (-not $ctx.kind -and $ctx.schema)
                {
                    # initialize
                    $inputObject = [ResourceManifest]::new($ctx.type, $ctx.description, $ctx.version)

                    $schemaParams = @{
                        Schema         = $ctx.schema
                        BuildHashTable = $BuildHashTable.IsPresent
                    }

                    # grab both embedded and schema key using ReadDscSchema
                    $resourceInput = ReadDscSchema @schemaParams

                    $inputObject.resourceInput = $resourceInput

                    Write-Verbose -Message "Adding '$($inputObject.type)'"
                    $objectBag.Add($inputObject)
                }

                if ($ctx.kind -eq 'Adapter' -and $ctx.type -in @('Microsoft.DSC/PowerShell', 'Microsoft.Windows/WindowsPowerShell'))
                {
                    $cacheRefresh = ReadDscPsAdapterSchema -BuildHashTable:$BuildHashTable.IsPresent

                    if ($cacheRefresh)
                    {
                        $cacheRefresh | ForEach-Object {
                            $m = [ResourceManifest]::new($_.Type, $_.Description, $_.Version, $_.ResourceInput)
                            $objectBag.Add($m)
                        }
                    }
                }
            }
            catch
            {
                # change to warning to debug argumentcompleter
                Write-Debug -Message ("'{0}' file could not be converted to JSON object. Error: {1}" -f $manifest.Name, $PSItem.Exception.Message)
            }
        }
    }

    end
    {
        Write-Verbose -Message ("Ended: {0}" -f $MyInvocation.MyCommand.Name)

        # return
        $objectBag
    }
}
#EndRegion '.\Private\DSC\ReadDscRequiredKey.ps1' 105
#Region '.\Private\DSC\ReadDscSchema.ps1' -1

function ReadDscSchema
{
    <#
    .SYNOPSIS
        Read DSC schema information.
 
    .DESCRIPTION
        The function ReadDscSchema reads the DSC schema information and builds the resource input required.
 
    .PARAMETER Schema
        The schema information to read.
 
    .PARAMETER BuildHashTable
        A switch parameter to indicate if the output should be a hashtable.
 
    .EXAMPLE
        PS C:\> $ctx = (Get-Content "$env:ProgramFiles\DSC\registry.dsc.resource.json" | ConvertFrom-Json)
        PS C:\> ReadDscSchema -Schema $ctx.schema
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [psobject]
        $Schema,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]
        $BuildHashTable
    )

    begin
    {
        Write-Verbose -Message ("Starting: {0}" -f $MyInvocation.MyCommand.Name)
    }

    process
    {
        $exampleCode = @()

        $schemaParams = @{
            schemaObject   = $null
            BuildHashTable = $BuildHashTable.IsPresent
        }

        if ($Schema.command)
        {
            # use resource name because WinGet does not allow direct resource to be called
            $resourceName = $ctx.type
            $process = GetNetProcessObject -Arguments "resource schema -r $resourceName"
            $out = StartNetProcessObject -Process $process

            if ($out.ExitCode -eq 0)
            {
                $schemaParams.schemaObject = $out.Output | ConvertFrom-Json
                $exampleCode = ReadDscSchemaProperty @schemaParams
            }
        }

        if ($Schema.embedded)
        {
            $schemaParams.schemaObject = $Schema.embedded
            $exampleCode = ReadDscSchemaProperty @schemaParams
        }
    }
    end
    {
        Write-Verbose -Message ("Ended: {0}" -f $MyInvocation.MyCommand.Name)
        # return
        return $exampleCode
    }
}
#EndRegion '.\Private\DSC\ReadDscSchema.ps1' 76
#Region '.\Private\DSC\ReadDscSchemaProperty.ps1' -1

function ReadDscSchemaProperty
{
    <#
    .SYNOPSIS
        Read DSC schema property information.
 
    .DESCRIPTION
        The function ReadDscSchemaProperty reads the DSC schema property information and builds the resource input required.
 
    .PARAMETER SchemaObject
        The schema object to pass in.
 
    .PARAMETER BuildHashTable
        A switch parameter to indicate if the output should be a hashtable.
 
    .EXAMPLE
        PS C:\> $ctx = (Get-Content "$env:ProgramFiles\DSC\registry.dsc.resource.json" | ConvertFrom-Json)
        PS C:\> $fullExePath = [System.String]::Concat("$(Split-Path -Path $exePath)\", $ctx.schema.command.executable, '.exe')
        PS C:\> $process = GetNetProcessObject -Arguments "$($ctx.schema.command.args)" -ExePath $fullExePath
        PS C:\> $schema = (StartNetProcessObject -Process $process).Output | ConvertFrom-Json
        PS C:\> ReadDscSchemaProperty -SchemaObject $schema
 
        Returns:
        {"keyPath":"<keyPath>"}
        {"valueName":"<valueName>","_exist":"<_exist>","keyPath":"<keyPath>","valueData":"<valueData>","_metadata":"<_metadata>"}
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.String])]
    param (
        [Parameter(Mandatory = $true)]
        [PSCustomObject]$SchemaObject,

        [Parameter()]
        [System.Management.Automation.SwitchParameter]$BuildHashTable
    )

    $exampleCode = @()

    # Add the required keys as example
    $exampleCode += $SchemaObject.required.ForEach({
            if ($BuildHashTable.IsPresent)
            {
                ConvertToHashString -HashTable ([ordered]@{ $_ = "<$($_)>" })
            }
            else
            {
                [ordered]@{ $_ = "<$($_)>" } | ConvertTo-Json -Depth 10 -Compress
            }
        })

    # Go through optional keys as example
    $props = $SchemaObject.properties.PSObject.Properties | Where-Object { $_.MemberType -eq 'NoteProperty' } | Select-Object -ExpandProperty Name

    $hash = [ordered]@{}
    foreach ($prop in $props)
    {
        $hash.Add($prop, "<$prop>")
    }

    if ($BuildHashTable.IsPresent)
    {
        $exampleCode += ConvertToHashString -HashTable $hash
    }
    else
    {
        if ($hash.Count -ne 0)
        {
            $exampleCode += ($hash | ConvertTo-Json -Compress)
        }
        else
        {
            $exampleCode += @()
        }
    }

    return $exampleCode
}
#EndRegion '.\Private\DSC\ReadDscSchemaProperty.ps1' 81
#Region '.\Private\DSC\RemoveDscConfigCommand.ps1' -1

function RemoveDscConfigCommand
{
    <#
    .SYNOPSIS
        Run delete config against DSC resource
 
    .DESCRIPTION
        The function RemoveDscResourceCommand deletes a DSC config
 
    .PARAMETER ResourceInput
        The resource input to provide. Supports JSON, path and PowerShell scripts.
 
    .PARAMETER Parameter
        Optionally, the parameter input to provide.
 
    .EXAMPLE
         PS C:\> RemoveDscConfigCommand -ResourceInput myconfig.dsc.config.yaml -Parameter myconfig.dsc.config.parameters.yaml
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [AllowNull()]
        [object]
        $ResourceInput,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [object]
        $Parameter
    )

    begin
    {
        # collect the command from index
        $commandData = GetDscCommandIndex -CommandName $MyInvocation.MyCommand.Name

        # build the input string for arguments
        $arguments = BuildDscInput -Command $commandData.Command -Operation $commandData.Operation -ResourceInput $ResourceInput -Parameter $Parameter
    }

    process
    {
        # get the System.Diagnostics.Process object
        $process = GetNetProcessObject -Arguments $arguments

        # start the process
        $inputObject = StartNetProcessObject -Process $process
    }

    end
    {
        # return
        $inputObject
    }
}
#EndRegion '.\Private\DSC\RemoveDscConfigCommand.ps1' 60
#Region '.\Private\DSC\RemoveDscResourceCommand.ps1' -1

function RemoveDscResourceCommand
{
    <#
    .SYNOPSIS
        Run delete operation against DSC resource
 
    .DESCRIPTION
        The function RemoveDscResourceCommand deletes a DSC resource
 
    .PARAMETER ResourceName
        The resource name to delete against.
 
    .PARAMETER ResourceInput
        The resource input to provide. Supports JSON, path and PowerShell scripts.
 
    .EXAMPLE
        PS C:\> RemoveDscResourceCommand -ResourceName Microsoft.Windows/Registry -ResourceInput @{keyPath = 'HKCU\1'}
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $ResourceName,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [object]
        $ResourceInput
    )

    begin
    {
        $commandData = GetDscCommandIndex -CommandName $MyInvocation.MyCommand.Name

        $arguments = BuildDscInput -Command $commandData.Command -Operation $commandData.Operation -ResourceInput $ResourceInput -ResourceName $ResourceName

        # TODO: we can still make a call to the resource manifest and see if input is required
    }

    process
    {
        # get the System.Diagnostics.Process object
        $process = GetNetProcessObject -Arguments $arguments

        # start the process
        if ($PSCmdlet.ShouldProcess(("'{0}' with input: [{1}]" -f $ResourceName, $resourceInput)))
        {
            $inputObject = StartNetProcessObject -Process $process
        }
    }

    end
    {
        # return
        $inputObject
    }
}
#EndRegion '.\Private\DSC\RemoveDscResourceCommand.ps1' 62
#Region '.\Private\DSC\ResolveDscExe.ps1' -1

function ResolveDscExe
{
    <#
    .SYNOPSIS
        Resolve the location of 'dsc.exe'.
 
    .DESCRIPTION
        The function ResolveDscExe searches for:
 
        * DSC_RESOURCE_PATH environment variable
        * PATH environment variable
        * Custom path specified using -Path
 
    .PARAMETER Path
        Optional parameter to directly specify 'dsc.exe'
 
    .PARAMETER Scope
        The scope to search for e.g. Machine, User, or Process
 
    .EXAMPLE
        PS C:\> $Exe = ResolveDscExe
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [OutputType([System.String])]
    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPositionalParameters', '', Justification = 'PowerShell module is 7+).')]
    Param
    (
        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [System.IO.FileInfo]
        $Path,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Machine', 'User', 'Process')]
        [System.String]
        $Scope = 'Machine'
    )

    if ($PSBoundParameters.ContainsKey('Path') -and ($Path.Name -ne 'dsc.exe'))
    {
        Throw "No file found at path '$Path'. Please specify the file path to 'dsc.exe'"
    }

    if ($IsWindows)
    {
        $dscResourceVar = GetEnvironmentVariable -Name 'DSC_RESOURCE_PATH' -Scope $Scope -Expanded
        if ($dscResourceVar.Length -ne 0)
        {
            $dscExePath = Join-Path -Path $dscResourceVar -ChildPath 'dsc.exe'

            if (Test-Path $dscExePath)
            {
                Write-Verbose -Message "Returning DSC executable from DSC_RESOURCE_PATH environment variable: $dscExePath."
                return $dscExePath
            }
        }

        $elevated = TestAdministrator
        $dscExePath = if ($elevated)
        {
            Join-Path $env:ProgramFiles 'dsc' 'dsc.exe'
        }
        else
        {
            Join-Path $env:LOCALAPPDATA 'dsc' 'dsc.exe'
        }

        if (Test-Path $dscexePath)
        {
            Write-Verbose -Message "Returning DSC executable from default installation path: $dscExePath."
            return $dscExePath
        }

        if (TestWinGetModule)
        {
            # TODO: life is difficult with WinGet
            $version = (GetDscVersion) -replace "preview.", ""
            $architecture = ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture).ToString().ToLower()
            $dscExePath = Join-Path $env:ProgramFiles 'WindowsApps' "Microsoft.DesiredStateConfiguration-Preview_3.0.$version.0_$architecture`__8wekyb3d8bbwe" 'dsc.exe'

            if (Test-Path $dscExePath)
            {
                Write-Verbose -Message "Returning DSC executable from WindowsApps: $dscExePath."
                return $dscExePath
            }
        }

        $dscExePath = (Get-Command -Name 'dsc.exe' -ErrorAction SilentlyContinue).Source
        if ($dscExePath)
        {
            Write-Verbose -Message "Returning DSC executable from PATH: $dscExePath."
            return $dscExePath
        }
        else
        {
            Throw "Could not locate 'dsc.exe'. Please make sure it can be found through the PATH or DSC_RESOURCE_PATH environment variable."
        }
    }
    elseif ($IsLinux)
    {
        $dscExePath = Join-Path '/opt/' 'microsoft' 'dsc' 'dsc.exe'
        if (Test-Path $dscExePath)
        {
            Write-Verbose -Message "Returning DSC executable from default installation path: $dscExePath."
            return $dscExePath
        }

        $dscExePath = (Get-Command -Name 'dsc.exe' -ErrorAction SilentlyContinue).Source
        if ($dscExePath)
        {
            Write-Verbose -Message "Returning DSC executable from PATH: $dscExePath."
            return $dscExePath
        }
        else
        {
            Throw "Could not locate 'dsc.exe'. Please make sure it can be found through the PATH or DSC_RESOURCE_PATH environment variable."
        }
    }
    elseif ($IsMacOs)
    {

    }
}
#EndRegion '.\Private\DSC\ResolveDscExe.ps1' 127
#Region '.\Private\DSC\SetDscConfigCommand.ps1' -1

function SetDscConfigCommand
{
    <#
    .SYNOPSIS
        Run set config against DSC resource
 
    .DESCRIPTION
        The function SetDscResourceCommand sets a DSC config
 
    .PARAMETER ResourceInput
        The resource input to provide. Supports JSON, path and PowerShell scripts.
 
    .PARAMETER Parameter
        Optionally, the parameter input to provide.
 
    .EXAMPLE
         PS C:\> SetDscConfigCommand -ResourceInput myconfig.dsc.config.yaml -Parameter myconfig.dsc.config.parameters.yaml
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [AllowNull()]
        [object]
        $ResourceInput,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [object]
        $Parameter
    )

    begin
    {
        # collect the command from index
        $commandData = GetDscCommandIndex -CommandName $MyInvocation.MyCommand.Name

        # build the input string for arguments
        $arguments = BuildDscInput -Command $commandData.Command -Operation $commandData.Operation -ResourceInput $ResourceInput -Parameter $Parameter
    }

    process
    {
        # get the System.Diagnostics.Process object
        $process = GetNetProcessObject -Arguments $arguments

        # start the process
        $inputObject = StartNetProcessObject -Process $process
    }

    end
    {
        # return
        $inputObject
    }
}
#EndRegion '.\Private\DSC\SetDscConfigCommand.ps1' 60
#Region '.\Private\DSC\SetDscResourceCommand.ps1' -1

function SetDscResourceCommand
{
    <#
    .SYNOPSIS
        Run set operation against DSC resource
 
    .DESCRIPTION
        The function SetDscResourceCommand sets a DSC resource
 
    .PARAMETER ResourceName
        The resource name to set against.
 
    .PARAMETER ResourceInput
        The resource input to provide. Supports JSON, path and PowerShell scripts.
 
    .EXAMPLE
        PS C:\> SetDscResourceCommand -ResourceName Microsoft.Windows/Registry -ResourceInput @{keyPath = 'HKCU\Microsoft'}
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $ResourceName,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [object]
        $ResourceInput
    )

    begin
    {
        $commandData = GetDscCommandIndex -CommandName $MyInvocation.MyCommand.Name

        $arguments = BuildDscInput -Command $commandData.Command -Operation $commandData.Operation -ResourceInput $ResourceInput -ResourceName $ResourceName

        # TODO: we can still make a call to the resource manifest and see if input is required
    }

    process
    {
        # get the System.Diagnostics.Process object
        $process = GetNetProcessObject -Arguments $arguments

        # start the process
        if ($PSCmdlet.ShouldProcess(("'{0}' with input: [{1}]" -f $ResourceName, $resourceInput)))
        {
            $inputObject = StartNetProcessObject -Process $process
        }
    }

    end
    {
        # return
        $inputObject
    }
}
#EndRegion '.\Private\DSC\SetDscResourceCommand.ps1' 62
#Region '.\Private\DSC\TestDsc.ps1' -1

function TestDsc
{
    <#
    .SYNOPSIS
        Check if dsc is installed.
 
    .DESCRIPTION
        Check if dsc is installed. Returns either true or false.
 
    .EXAMPLE
        PS C:\> TestDsc
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param ()

    $dsc = (Get-Command dsc -ErrorAction SilentlyContinue)
    if ($dsc)
    {
        $true
    }
    else
    {
        $false
    }
}
#EndRegion '.\Private\DSC\TestDsc.ps1' 30
#Region '.\Private\DSC\TestDscConfigCommand.ps1' -1

function TestDscConfigCommand
{
    <#
    .SYNOPSIS
        Run test config against DSC resource
 
    .DESCRIPTION
        The function TestDscResourceCommand tests a DSC config
 
    .PARAMETER ResourceInput
        The resource input to provide. Supports JSON, path and PowerShell scripts.
 
    .PARAMETER Parameter
        Optionally, the parameter input to provide.
 
    .EXAMPLE
         PS C:\> TestDscConfigCommand -ResourceInput myconfig.dsc.config.yaml -Parameter myconfig.dsc.config.parameters.yaml
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [AllowNull()]
        [object]
        $ResourceInput,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [object]
        $Parameter
    )

    begin
    {
        # collect the command from index
        $commandData = GetDscCommandIndex -CommandName $MyInvocation.MyCommand.Name

        # build the input string for arguments
        $arguments = BuildDscInput -Command $commandData.Command -Operation $commandData.Operation -ResourceInput $ResourceInput -Parameter $Parameter
    }

    process
    {
        # get the System.Diagnostics.Process object
        $process = GetNetProcessObject -Arguments $arguments

        # start the process
        $inputObject = StartNetProcessObject -Process $process
    }

    end
    {
        # return
        $inputObject
    }
}
#EndRegion '.\Private\DSC\TestDscConfigCommand.ps1' 60
#Region '.\Private\DSC\TestDscResourceCommand.ps1' -1

function TestDscResourceCommand
{
    <#
    .SYNOPSIS
        Run test operation against DSC resource
 
    .DESCRIPTION
        The function TestDscResourceCommand tests a DSC resource
 
    .PARAMETER ResourceName
        The resource name to test against.
 
    .PARAMETER ResourceInput
        The resource input to provide. Supports JSON, path and PowerShell scripts.
 
    .EXAMPLE
        PS C:\> TestDscResourceCommand -ResourceName Microsoft.Windows/Registry -ResourceInput @{keyPath = 'HKCU\Microsoft'}
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $ResourceName,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [object]
        $ResourceInput
    )

    begin
    {
        $commandData = GetDscCommandIndex -CommandName $MyInvocation.MyCommand.Name

        $arguments = BuildDscInput -Command $commandData.Command -Operation $commandData.Operation -ResourceInput $ResourceInput -ResourceName $ResourceName

        # TODO: we can still make a call to the resource manifest and see if input is required
    }

    process
    {
        # get the System.Diagnostics.Process object
        $process = GetNetProcessObject -Arguments $arguments

        # start the process
        $inputObject = StartNetProcessObject -Process $process
    }

    end
    {
        # return
        $inputObject
    }
}
#EndRegion '.\Private\DSC\TestDscResourceCommand.ps1' 59
#Region '.\Private\GitHub\GetGitHubReleaseVersion.ps1' -1

function GetGithubReleaseVersion
{
    <#
    .SYNOPSIS
        Get GitHub release version using API.
 
    .DESCRIPTION
        The function GetGithubReleaseVersion gets a GitHub release using the API.
 
    .PARAMETER Organization
        The organization name to look for.
 
    .PARAMETER Repository
        The repository name to look for in the organization.
 
    .PARAMETER Latest
        Switch to grab latest version if available.
 
    .EXAMPLE
        PS C:\> GetGitHubReleaseVersion -Organization PowerShell -Repository DSC
 
        Returns a list of available versions for DSC repository
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.String])]
    param(
        [Parameter(Mandatory = $true)]
        [System.String]
        $Organization,

        [Parameter(Mandatory = $true)]
        [System.String]
        $Repository,

        [System.Management.Automation.SwitchParameter]
        $Latest
    )
    $Url = 'https://api.github.com/repos/{0}/{1}/releases' -f $Organization, $Repository
    if ($Latest.IsPresent)
    {
        $Url = '{0}/latest' -f $Url
    }
    try
    {
        $Versions = Invoke-RestMethod -Uri $Url -ErrorAction 'Stop'
        # TODO: when versions become version, change to system.version
        return ($versions.tag_name | Foreach-Object -Process { $_.TrimStart("v") -as [System.String] })
    }
    catch
    {
        Write-Error -Message "Could not get version of $Organization/$Repository from GitHub. $_" -Category ObjectNotFound
    }
}
#EndRegion '.\Private\GitHub\GetGitHubReleaseVersion.ps1' 57
#Region '.\Private\Path\AddToPath.ps1' -1

function AddToPath
{
    <#
    .SYNOPSIS
        Adds the specified path to PATH env variable
 
    .DESCRIPTION
        Assumes that paths on PATH are separated by `;`
 
    .PARAMETER Path
        path to add to the list.
 
    .PARAMETER Persistent
        save the variable in machine scope.
 
    .PARAMETER First
        preppend the value instead of appending.
 
    .PARAMETER User
        save to user scope. use only when needed.
 
    .EXAMPLE
        PS C:\> $exePath | AddToPath -Persistent:$true
 
    .NOTES
        Site: https://github.com/qbikez/ps-pathutils/tree/master
    #>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidMultipleTypeAttributes', '', Justification = 'Not my store.')]
    param
    (
        [Parameter(ValueFromPipeline = $true, mandatory = $true)]
        [System.String]
        $Path,

        [Alias("p")]
        [System.Management.Automation.SwitchParameter]
        $Persistent,

        [System.Management.Automation.SwitchParameter]
        $First,

        [System.Management.Automation.SwitchParameter]
        [System.Boolean]
        $User
    )

    process
    {
        if ($null -eq $path)
        {
            throw [System.ArgumentNullException]"path"
        }
        if ($User)
        {
            $p = GetPathEnv -User
        }
        elseif ($persistent)
        {
            $p = GetPathEnv -Machine
        }
        else
        {
            $p = GetPathEnv -Current
        }
        $p = $p | ForEach-Object { $_.trimend("\") }
        $p = @($p)
        $paths = @($path)
        $paths | ForEach-Object {
            $path = $_.trimend("\")
            Write-Verbose "adding $path to PATH"
            if ($first)
            {
                if ($p.length -eq 0 -or $p[0] -ine $path)
                {
                    $p = @($path) + $p
                }
            }
            else
            {
                if ($path -inotin $p)
                {
                    $p += $path
                }
            }
        }

        if ($User)
        {
            Write-Verbose "saving user PATH and adding to current proc"
            [System.Environment]::SetEnvironmentVariable("PATH", [string]::Join(";", $p), [System.EnvironmentVariableTarget]::User);
            #add also to process PATH
            AddToPath $path -persistent:$false -first:$first
        }
        elseif ($persistent)
        {
            Write-Verbose "Saving to global machine PATH variable"
            [System.Environment]::SetEnvironmentVariable("PATH", [string]::Join(";", $p), [System.EnvironmentVariableTarget]::Machine);
            #add also to process PATH
            AddToPath $path -persistent:$false -first:$first
        }
        else
        {
            $env:path = [string]::Join(";", $p);
            [System.Environment]::SetEnvironmentVariable("PATH", $env:path, [System.EnvironmentVariableTarget]::Process);
        }
    }
}
#EndRegion '.\Private\Path\AddToPath.ps1' 109
#Region '.\Private\Path\GetEnvironmentPath.ps1' -1

function GetEnvironmentPath
{
    <#
    .SYNOPSIS
        Get the environment path.
 
    .DESCRIPTION
        The function GetEnvironmentPath gets the environment path.
 
    .PARAMETER Scope
        [System.EnvironmentVariableTarget], [String]
        Specify the scope to search for the target Environment Variable.
 
        Process : Environment Variables in the running process.
 
        User : Environment Variables in the User Scope affect the Global Environment Variables for the current user.
 
        Machine : Environment Variables in the Machine Scope change the settings in the registry for all users.
 
    .EXAMPLE
        PS C:\> GetEnvironmentPath -Scope Machine
 
    .NOTES
        https://github.com/rbleattler/xEnvironmentVariables
    #>

    param (
        [Parameter(Mandatory)]
        [System.EnvironmentVariableTarget]
        $Scope
    )
    switch ($Scope)
    {
        "Process"
        {
            "Env:"
        }
        "User"
        {
            "Registry::HKEY_CURRENT_USER\Environment"
        }
        "Machine"
        {
            "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
        }
        Default
        {
            $null
        }
    }
}
#EndRegion '.\Private\Path\GetEnvironmentPath.ps1' 51
#Region '.\Private\Path\GetEnvironmentVariable.ps1' -1

function GetEnvironmentVariable
{
    <#
    .SYNOPSIS
    A PowerShell module to handle environment variables, supporting variable expansion. This function handles GETTING environment variables.
 
    .DESCRIPTION
    This module is capable of Retrieving Environment Variables in any scope (Process, User, Machine). It will return the value of the Envrionment Variable.
 
    .PARAMETER Name
    [String] Specify the name of the Environment Variable to retrieve.
 
    .PARAMETER Scope
    [System.EnvironmentVariableTarget], [String]
    Specify the scope to search for the target Environment Variable.
 
    Process : Environment Variables in the running process.
 
    User : Environment Variables in the User Scope affect the Global Environment Variables for the current user.
 
    Machine : Environment Variables in the Machine Scope change the settings in the registry for all users.
 
    .PARAMETER Expanded
    [Switch]
    If enabled any Environment Variables in the output of the command will be expanded.
    String : A simple string value
    ExpandString : A string value which contains unexpanded environment variables in the syntax of %VARIABLENAME%. These variables will NOT be expanded when the value is read.
 
    .PARAMETER ShowProperties
    [Switch] If enabled this parameter will show the Name, Value, Scope, ValueType and (if a String containing an unexpanded Environment Variable) the BeforeExpansion properties and their respective values. Otherwise only the Value will be output as a string.
 
    .EXAMPLE
    PS C:\> Get-EnvironmentVariable -name TestVar -Scope Machine -ShowProperties
 
    Name : TestVar
    Value : TestValue
    Scope : Machine
    ValueType : String
    BeforeExpansion : TestValue
 
    .EXAMPLE
    PS C:\> Get-EnvironmentVariable -name TestPathVar -Scope Machine -ShowProperties
 
    Name : TestPathVar
    Value : C:\Users\rblea\AppData\Local\Temp\TestValue2
    Scope : Machine
    ValueType : String
    BeforeExpansion : %TEMP%\TestValue2
 
    .EXAMPLE
    PS C:\> Get-EnvironmentVariable -name TestPathVar -Scope Machine
    C:\Users\USER\AppData\Local\Temp\TestValue2
 
    .EXAMPLE
    PS C:\> Get-EnvironmentVariable -name TestPathVar -Scope Machine -Expanded
    %TEMP%\TestValue2
 
 
    .INPUTS
    [String],[System.EnvironmentVariableTarget],[Boolean]
 
    .OUTPUTS
    [Hashtable],[String]
 
    .NOTES
 
    .LINK
    https://github.com/rbleattler/xEnvironmentVariables
 
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [String]
        $Name,

        [Parameter()]
        [System.EnvironmentVariableTarget]
        $Scope = [System.EnvironmentVariableTarget]::Process,

        [Parameter()]
        [Switch]
        $Expanded,

        [Parameter()]
        [Switch]
        $ShowProperties
    )

    $Getter = [System.Environment]::GetEnvironmentVariable($Name, $Scope)
    if ($null -eq $Getter)
    {
        $RawValue = $null
        $GetterType = $null
    }
    else
    {
        if ($Scope -ne "Process")
        {
            if (!$Expanded)
            {
                $AllEnvironmentVariables = Get-Item -Path (GetEnvironmentPath -Scope $Scope)
                $GetterType = $AllEnvironmentVariables.GetValueKind($Name)
            }
            else
            {
                $AllEnvironmentVariables = [System.Environment]::GetEnvironmentVariables($Scope)
                $GetterType = $Getter.GetTypeCode()
            }
            if ($GetterType -eq "ExpandString")
            {
                $RawValue = $AllEnvironmentVariables.GetValue(
                    $Name, $null, 'DoNotExpandEnvironmentNames'
                )
            }
            elseif ($GetterType -eq "String")
            {
                $RawValue = $Getter
                if ($Expanded)
                {
                    $Getter = [System.Environment]::ExpandEnvironmentVariables($Getter)
                }
            }
            else
            {
                # inappropriate kind (dword, bytes, ...)
                $RawValue = $null
                $GetterType = $null
            }
        }
        else
        {
            # $Scope -eq "Process"
            $RawValue = $null
            $GetterType = "String"
        }
    }
    $params = @{
        Name            = $Name
        Value           = $Getter
        Scope           = $Scope
        ValueType       = $GetterType
        BeforeExpansion = $RawValue
    }
    $null = NewEnvironmentVariableObject @params | Set-Variable -Name NewEnvVar

    if ($ShowProperties)
    {
        $NewEnvVar | Add-Member ScriptMethod ToString { $this.Value } -Force -PassThru
    }
    else
    {
        if (!$Expanded)
        {
            $NewEnvVar | Add-Member ScriptMethod ToString { $this.Value } -Force -PassThru | Select-Object -ExpandProperty BeforeExpansion
        }
        else
        {
            $NewEnvVar | Add-Member ScriptMethod ToString { $this.Value } -Force -PassThru | Select-Object -ExpandProperty Value
        }
    }
}
#EndRegion '.\Private\Path\GetEnvironmentVariable.ps1' 164
#Region '.\Private\Path\GetEnvVar.ps1' -1

function GetEnvVar
{
    <#
    .SYNOPSIS
        Get Environment variable value.
 
    .DESCRIPTION
        Get Environment variable value. To see more details, check out the .NOTES section for link.
 
    .PARAMETER Name
        The variable name to look for.
 
    .PARAMETER User
        get variable from user scope (persistent)
 
    .PARAMETER Machine
        get variable from machine scope (persistent)
 
    .PARAMETER Current
        (default=true) get variable from current process scope
 
    .EXAMPLE
        PS C:\> $machinepath = getenvvar "PATH" -machine
 
    .NOTES
        Site: https://github.com/qbikez/ps-pathutils/tree/master
    #>

    [CmdletBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '', Justification = 'Not my store.')]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $Name,

        [System.Management.Automation.SwitchParameter]
        $User,

        [System.Management.Automation.SwitchParameter]
        $Machine,

        [System.Management.Automation.SwitchParameter]
        $Current
    )

    $val = @()
    if ($user)
    {
        $val += [System.Environment]::GetEnvironmentVariable($name, [System.EnvironmentVariableTarget]::User);
    }
    if ($machine)
    {
        $val += [System.Environment]::GetEnvironmentVariable($name, [System.EnvironmentVariableTarget]::Machine);
    }
    if (!$user.IsPresent -and !$machine.IsPresent)
    {
        $current = $true
    }
    if ($current)
    {
        $val = invoke-expression "`$env:$name"
    }
    if ($null -ne $val)
    {
        $p = $val -Split ";"
    }
    else
    {
        $p = @()
    }

    return $p
}
#EndRegion '.\Private\Path\GetEnvVar.ps1' 74
#Region '.\Private\Path\GetPathEnv.ps1' -1

function GetPathEnv
{
    <#
    .SYNOPSIS
        Gets PATH env variable
 
    .DESCRIPTION
        Gets PATH env variable. To see more details, check out the .NOTES section for link.
 
    .PARAMETER User
        Get the value from user scope
 
    .PARAMETER Machine
        (default) Get the value from machine scope
 
    .PARAMETER Current
        Get the value from process scope
 
    .PARAMETER All
        Return values for each scope
 
    .EXAMPLE
        PS C:\> $p = GetPathEnv -Machine
 
    .NOTES
        Site: https://github.com/qbikez/ps-pathutils/tree/master
    #>


    [CmdLetBinding(DefaultParameterSetName = "scoped")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '', Justification = 'Not my store.')]
    param
    (
        [Parameter(ParameterSetName = "scoped")]
        [System.Management.Automation.SwitchParameter]
        $User,

        [Parameter(ParameterSetName = "scoped")]
        [System.Management.Automation.SwitchParameter]
        $Machine,

        [Alias("process")]
        [Parameter(ParameterSetName = "scoped")]
        [System.Management.Automation.SwitchParameter]
        $Current,

        [Parameter(ParameterSetName = "all")]
        [System.Management.Automation.SwitchParameter]
        $All
    )

    $scopespecified = $user.IsPresent -or $machine.IsPresent -or $current.IsPresent
    $path = @()
    $userpath = getenvvar "PATH" -user
    if ($user)
    {
        $path += $userpath
    }
    $machinepath = getenvvar "PATH" -machine
    if ($machine -or !$scopespecified)
    {
        $path += $machinepath
    }
    if (!$user.IsPresent -and !$machine.IsPresent)
    {
        $current = $true
    }
    $currentPath = getenvvar "PATH" -current
    if ($current)
    {
        $path = $currentPath
    }

    if ($all)
    {
        $h = @{
            user    = $userpath
            machine = $machinepath
            process = $currentPath
        }
        return @(
            "`r`n USER",
            " -----------",
            $h.user,
            "`r`n MACHINE",
            " -----------",
            $h.machine,
            "`r`n PROCESS",
            " -----------",
            $h.process
        )
    }

    return $path
}
#EndRegion '.\Private\Path\GetPathEnv.ps1' 95
#Region '.\Private\Path\NewEnvironmentVariableObject.ps1' -1

function NewEnvironmentVariableObject
{
    <#
    .SYNOPSIS
    A PowerShell module to handle environment variables, supporting variable expansion. This function handles GETTING environment variables.
 
    .DESCRIPTION
    This module is capable of Retrieving Environment Variables in any scope (Process, User, Machine). It will return the value of the Envrionment Variable.
 
    .PARAMETER Name
    [String] Specify the name of the Environment Variable to retrieve.
 
    .PARAMETER Value
        What is the value to be set.
 
    .PARAMETER Scope
    [System.EnvironmentVariableTarget], [String]
    Specify the scope to search for the target Environment Variable.
 
    Process : Environment Variables in the running process.
 
    User : Environment Variables in the User Scope affect the Global Environment Variables for the current user.
 
    Machine : Environment Variables in the Machine Scope change the settings in the registry for all users.
 
    .PARAMETER ValueType
        The value type e.g. String.
 
    .PARAMETER BeforeExpansion
        The value before it got expanded.
 
    .EXAMPLE
        PS C:\> NewEnvironmentVariableObject
 
    .NOTES
        Site: https://github.com/rbleattler/xEnvironmentVariables
    #>

    param
    (
        [OutputType([HashTable])]
        [Parameter(Mandatory)]
        [ValidatePattern("[^=]+")]
        $Name,
        [Parameter()]
        [AllowNull()]
        [String]
        $Value,
        [Parameter(Mandatory)]
        [System.EnvironmentVariableTarget]
        $Scope,
        [Parameter()]
        [AllowNull()]
        [ValidateSet("String", "ExpandString", $null)]
        [String]
        $ValueType,
        [Parameter()]
        [AllowNull()]
        [String]
        $BeforeExpansion
    )

    $OutPut = [PSCustomObject]@{
        Name            = $Name
        Value           = $Value
        Scope           = $Scope
        ValueType       = $ValueType
        BeforeExpansion = $BeforeExpansion
    }
    $OutPut
}
#EndRegion '.\Private\Path\NewEnvironmentVariableObject.ps1' 71
#Region '.\Private\Process\GetNetProcessObject.ps1' -1

function GetNetProcessObject
{
    <#
    .SYNOPSIS
        Produce .NET System.Diagnostics.Process object.
 
    .DESCRIPTION
        The function GetNetProcessObject instantiaties a .NET process object to be returned.
 
    .PARAMETER Arguments
        The arguments to add to the run.
 
    .PARAMETER ExePath
        The executable to start. Defaults to 'dsc.exe'
 
    .EXAMPLE
        PS C:\> GetNetProcessObject -Arguments 'resource get --resource Microsoft.Windows/RebootPending'
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.Diagnostics.Process])]
    param
    (
        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [System.String]
        $Arguments,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [System.String]
        $ExePath = (ResolveDscExe)
    )

    # use System.Diagnostics.Process instead of & or Invoke-Expression
    $proc = [System.Diagnostics.Process]::new()
    $arguments = (-not [string]::IsNullOrEmpty($Arguments)) ? $Arguments : '--version'

    # build process start info with redirects
    $procStartinfo = [System.Diagnostics.ProcessStartInfo]::new($ExePath, $arguments)
    $procStartinfo.UseShellExecute = $false
    $procStartinfo.RedirectStandardOutput = $true
    $procStartinfo.RedirectStandardError = $true

    $proc.StartInfo = $procStartinfo

    # return object
    return $proc
}
#EndRegion '.\Private\Process\GetNetProcessObject.ps1' 52
#Region '.\Private\Process\StartNetProcessObject.ps1' -1

function StartNetProcessObject
{
    <#
    .SYNOPSIS
        Start process using .NET.
 
    .DESCRIPTION
        The function StartNetProcessObject starts a process using .NET instead of Start-Process cmdlet.
 
    .PARAMETER Process
        The process object to start.
 
    .EXAMPLE
        PS C:\> $Process = [System.Diagnostics.Process]::new()
        PS C:\> $ProcStartinfo = [System.Diagnostics.ProcessStartInfo]::new('dsc.exe', '--version')
        PS C:\> $Process.StartInfo = $ProcStartInfo
        PS C:\> StartNetProcessObject -Process $Process
 
        Runs dsc --version
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [System.Diagnostics.Process]
        $Process
    )

    Write-Debug -Message ("Starting '{0}' with arguments: [{1}]" -f $Process.StartInfo.FileName, $Process.StartInfo.Arguments)
    $Process.Start() | Out-Null

    $output = [System.Collections.Generic.List[string]]::new()

    if ($Process.StartInfo.RedirectStandardOutput)
    {
        while ($null -ne ($line = $Process.StandardOutput.ReadLine()))
        {
            if (-not [string]::IsNullOrEmpty($line))
            {
                Write-Debug "Adding: $line"
                $output.Add($line)
            }
        }
    }

    if ($Process.StartInfo.RedirectStandardError)
    {
        $standardError = $Process.StandardError.ReadToEnd()
    }

    $Process.WaitForExit()

    $inputObject = New-Object -TypeName PSObject -Property ([Ordered]@{
            Executable = $Process.StartInfo.FileName
            Arguments  = $Process.StartInfo.Arguments
            ExitCode   = $Process.ExitCode
            Output     = $output
            Error      = $standardError
        })

    $inputObject
}
#EndRegion '.\Private\Process\StartNetProcessObject.ps1' 67
#Region '.\Private\Util\ConvertToHashString.ps1' -1

Function ConvertToHashString
{
    <#
    .SYNOPSIS
        Convert hashtable to string.
 
    .DESCRIPTION
        The function ConvertToHashString converts a hashtable to a string.
 
    .PARAMETER HashTable
        The hashtable to convert.
 
    .EXAMPLE
        PS C:\> ConvertToHashString -HashTable @{ 'key' = 'value' }
 
        Returns:
        @{key = value}
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.Collections.Specialized.OrderedDictionary]$HashTable
    )
    $first = $true
    foreach ($pair in $HashTable.GetEnumerator())
    {
        if ($first)
        {
            $first = $false
        }
        else
        {
            $output += ';'
        }

        $output += "'{0}' = '{1}'" -f $($pair.key), $($pair.Value)
    }

    $output = [System.String]::Concat('@{', $output, '}')

    return $output
}
#EndRegion '.\Private\Util\ConvertToHashString.ps1' 48
#Region '.\Private\Util\GetBoundParameters.ps1' -1

function GetBoundParameters
{
    <#
    .SYNOPSIS
        Helper function that returns a hashtable of the GOOD bound parameters needed for calling other function
 
    .DESCRIPTION
        The function GetBoundParameters helps to create a hash table used for other functions
 
    .PARAMETER BoundParameters
        The bound parameters to check
 
    .PARAMETER GoodKeys
        The good keys to check in bound parameters
 
    .EXAMPLE
        PS C:\> GetBoundParameters -BoundParameters $PSBoundParameters -GoodKeys @("ResourceName", "ResourceInput")
 
 
    .NOTES
        Tags: Parameters
    #>

    [OutputType([hashtable])]
    [CmdletBinding()]
    Param (
        [AllowNull()]
        [object]
        $BoundParameters,

        [System.Array]
        $GoodKeys
    )

    $ConfigurationHelper = @{}

    foreach ($Key in $BoundParameters.Keys)
    {
        if ($GoodKeys -contains $Key)
        {
            $ConfigurationHelper[$Key] = $BoundParameters[$Key]
        }
    }

    return $ConfigurationHelper
}
#EndRegion '.\Private\Util\GetBoundParameters.ps1' 46
#Region '.\Private\Util\TestAdministrator.ps1' -1

function TestAdministrator
{
    <#
    .SYNOPSIS
        Test if user is currently running elevated or not.
 
    .DESCRIPTION
        Test if user is currently running elevated or not.
 
    .EXAMPLE
        TestAdministrator
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>


    $user = [Security.Principal.WindowsIdentity]::GetCurrent();
    (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
#EndRegion '.\Private\Util\TestAdministrator.ps1' 20
#Region '.\Private\Util\TestWinGetModule.ps1' -1

function TestWinGetModule
{
    <#
    .SYNOPSIS
        Checks if the specified PowerShell module is installed.
 
    .DESCRIPTION
        The TestWinGetModule function checks if a specified PowerShell module is installed on the system by using the Get-Module cmdlet with the -ListAvailable parameter.
        By default, it checks for the "Microsoft.WinGet.Dsc" module, but you can specify a different module name if needed.
 
    .PARAMETER ModuleName
        The name of the module to check for. The default value is "Microsoft.WinGet.Dsc".
 
    .EXAMPLE
        TestWinGetModule
        This example checks if the "Microsoft.WinGet.Dsc" module is installed.
 
    .EXAMPLE
        PS C:\> TestWinGetModule -ModuleName "Pester"
        This example checks if the "Pester" module is installed.
 
    .OUTPUTS
        System.Boolean
        Returns $true if the module is installed, otherwise returns $false.
    #>

    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param (
        [Parameter(Mandatory = $false)]
        [string]$ModuleName = "Microsoft.WinGet.Dsc"
    )

    process
    {
        $module = Get-Module -Name $ModuleName -ListAvailable
        if ($module)
        {
            return $true
        }
        else
        {
            return $false
        }
    }
}
#EndRegion '.\Private\Util\TestWinGetModule.ps1' 46
#Region '.\Private\Util\TestYamlModule.ps1' -1

function TestYamlModule
{
    <#
    .SYNOPSIS
        Test if ConvertTo-Yaml is installed from Yayaml
 
    .DESCRIPTION
        Test if ConvertTo-Yaml is installed from Yayaml
 
    .EXAMPLE
        TestYamlModule
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>


    if (-not (Get-Module -ListAvailable yayaml -ErrorAction SilentlyContinue) -or (Get-Module -ListAvailable powershell-yaml))
    {
        return $false
    }

    return $true
}
#EndRegion '.\Private\Util\TestYamlModule.ps1' 24
#Region '.\Public\ConvertTo-DscJson.ps1' -1

function ConvertTo-DscJson
{
    <#
    .SYNOPSIS
        Convert DSC Configuration (v1/v2) Document to JSON.
 
    .DESCRIPTION
        The function ConvertTo-DscJson converts a DSC Configuration Document (v1/v2) to JSON.
 
    .PARAMETER Path
        The file path to a valid DSC Configuration Document.
 
    .PARAMETER Content
        The content to a valid DSC Configuration Document.
 
    .EXAMPLE
        PS C:\> $path = 'myConfig.ps1'
        PS C:\> ConvertTo-DscJson -Path $path
 
    .INPUTS
        Input a valid DSC Configuration Document
 
        configuration MyConfiguration {
            Import-DscResource -ModuleName PSDesiredStateConfiguration
            Node localhost
            {
                Environment CreatePathEnvironmentVariable
                {
                    Name = 'TestPathEnvironmentVariable'
                    Value = 'TestValue'
                    Ensure = 'Present'
                    Path = $true
                    Target = @('Process')
                }
            }
        }
 
    .OUTPUTS
        Returns a JSON string
 
        {
            "$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json",
            "resources": {
                "name": "MyConfiguration node",
                "type": "Microsoft.DSC/PowerShell",
                "properties": {
                "resources": [
                    {
                    "name": "CreatePathEnvironmentVariable",
                    "type": "PSDscResources/Environment",
                    "properties": {
                        "Value": "TestValue",
                        "Path": true,
                        "Name": "TestPathEnvironmentVariable",
                        "Ensure": "Present",
                        "Target": [
                                    "Process"
                                ]
                            }
                        }
                    ]
                }
            }
        }
    #>

    [CmdletBinding(DefaultParameterSetName = 'Path')]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory = $true,
            ParameterSetName = 'Path')]
        [ValidateScript({
                if (-Not ($_ | Test-Path) )
                {
                    throw "File or folder does not exist"
                }
                if (-Not ($_ | Test-Path -PathType Leaf) )
                {
                    throw "The Path argument must be a file. Folder paths are not allowed."
                }
                return $true
            })]
        [System.String]
        $Path,

        [Parameter(Mandatory = $true,
            ParameterSetName = 'Content')]
        [System.String]
        $Content
    )

    begin
    {
        Write-Verbose -Message ("Starting: {0}" -f $MyInvocation.MyCommand.Name)
    }

    process
    {
        $configurationDocument = BuildDscConfigDocument @PSBoundParameters
    }

    end
    {
        Write-Verbose ("Ended: {0}" -f $MyInvocation.MyCommand.Name)
        return ($configurationDocument | ConvertTo-Json -Depth 10 -Compress)
    }
}
#EndRegion '.\Public\ConvertTo-DscJson.ps1' 108
#Region '.\Public\ConvertTo-DscYaml.ps1' -1

function ConvertTo-DscYaml
{
    <#
    .SYNOPSIS
        Convert DSC Configuration (v1/v2) Document to YAML.
 
    .DESCRIPTION
        The function ConvertTo-DscYaml converts a DSC Configuration Document (v1/v2) to YAML.
 
    .PARAMETER Path
        The file path to a valid DSC Configuration Document.
 
    .PARAMETER Content
        The content to a valid DSC Configuration Document.
 
    .EXAMPLE
        PS C:\> $path = 'myConfig.ps1'
        PS C:\> ConvertTo-DscYaml -Path $path
 
    .INPUTS
        Input a valid DSC Configuration Document
 
        configuration MyConfiguration {
            Import-DscResource -ModuleName PSDesiredStateConfiguration
            Node localhost
            {
                Environment CreatePathEnvironmentVariable
                {
                    Name = 'TestPathEnvironmentVariable'
                    Value = 'TestValue'
                    Ensure = 'Present'
                    Path = $true
                    Target = @('Process')
                }
            }
        }
 
    .OUTPUTS
        Returns a YAML string
        $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
        resources:
            name: MyConfiguration
            type: Microsoft.DSC/PowerShell
            properties:
                resources:
                - name: CreatePathEnvironmentVariable
                type: PSDscResources/Environment
                properties:
                    Value: TestValue
                    Path: true
                    Name: TestPathEnvironmentVariable
                    Ensure: Present
                    Target:
                    - Process
    #>

    [CmdletBinding(DefaultParameterSetName = 'Path')]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory = $true,
            ParameterSetName = 'Path')]
        [ValidateScript({
                if (-Not ($_ | Test-Path) )
                {
                    throw "File or folder does not exist"
                }
                if (-Not ($_ | Test-Path -PathType Leaf) )
                {
                    throw "The Path argument must be a file. Folder paths are not allowed."
                }
                return $true
            })]
        [System.String]
        $Path,

        [Parameter(Mandatory = $true,
            ParameterSetName = 'Content')]
        [System.String]
        $Content
    )

    begin
    {
        Write-Verbose -Message ("Starting: {0}" -f $MyInvocation.MyCommand.Name)
    }

    process
    {
        $configurationDocument = BuildDscConfigDocument @PSBoundParameters
    }

    end
    {
        Write-Verbose ("Ended: {0}" -f $MyInvocation.MyCommand.Name)
        if (TestYamlModule)
        {
            $inputObject = ConvertTo-Yaml -InputObject $configurationDocument -Depth 10
        }
        return $inputObject
    }
}
#EndRegion '.\Public\ConvertTo-DscYaml.ps1' 102
#Region '.\Public\Get-PsDscResourceSchema.ps1' -1

<#
.SYNOPSIS
    Retrieves the schema for a specified DSC resource.
 
.DESCRIPTION
    The function Get-PsDscResourceSchema function retrieves the schema for a specified Desired State Configuration (DSC) resource.
    It can optionally include the properties of the resource in the output.
 
.PARAMETER ResourceName
    The name of the DSC resource for which to retrieve the schema.
 
.PARAMETER IncludeProperty
    A switch parameter that, when specified, includes the properties of the DSC resource in the output.
 
.EXAMPLE
    PS C:\> Get-PsDscResourceSchema -ResourceName "Microsoft.Windows/Registry"
    Retrieves the schema for the "Microsoft.Windows/Registry" DSC resource.
 
.EXAMPLE
    PS C:\> Get-PsDscResourceSchema -ResourceName "Microsoft.WinGet.DSC/WinGetPackage" -IncludeProperty
    Retrieves the schema for the "Microsoft.WinGet.DSC/WinGetPackage" DSC resource and includes its properties in the output only.
 
.NOTES
    For more details, refer to the module documentation.
#>

function Get-PsDscResourceSchema
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [Alias('Name')]
        [ArgumentCompleter([DscResourceCompleter])]
        [System.String]
        $ResourceName,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]
        $IncludeProperty
    )

    begin
    {
        Write-Verbose -Message ("Starting: {0}" -f $MyInvocation.MyCommand.Name)
    }

    process
    {
        $process = GetNetProcessObject -Arguments "resource schema --resource $ResourceName"

        $inputObject = StartNetProcessObject -Process $process

        if (-not ([string]::IsNullOrEmpty($inputObject.Output)))
        {
            $out = $inputObject.Output | ConvertFrom-Json

            if ($IncludeProperty)
            {
                $hash = @{}
                $out.properties.PSObject.Members | ForEach-Object {
                    if ($_.MemberType -eq 'NoteProperty ')
                    {
                        $hash[$_.Name] = $null
                    }
                }

                $inputObject = $hash
            }
        }
    }

    end
    {
        Write-Verbose ("Ended: {0}" -f $MyInvocation.MyCommand.Name)
        return $inputObject
    }
}
#EndRegion '.\Public\Get-PsDscResourceSchema.ps1' 78
#Region '.\Public\Initialize-PsDscConfigDocument.ps1' -1

function Initialize-PsDscConfigDocument
{
    <#
    .SYNOPSIS
        Initialize a DSC configuration document.
 
    .DESCRIPTION
        The function Initialize-PsDscConfigDocument initializes a DSC configuration document. It can pick up 'resource' and 'adapter' from PowerShell or Windows PowerShell.
 
    .PARAMETER ResourceName
        The resource name to execute.
 
    .PARAMETER ResourceInput
        The resource input to provide. Supports PowerShell hash table.
 
    .PARAMETER ResourceDescription
        The resource description to provide.
 
    .PARAMETER IsPwsh
        Switch to indicate if the resource is using PowerShell. Adds 'Microsoft.DSC/PowerShell' type.
 
    .PARAMETER IsWindowsPowerShell
        Switch to indicate if the resource is using Windows PowerShell. Adds 'Microsoft.Windows/WindowsPowerShell' type.
 
    .EXAMPLE
        PS C:\> Initialize-PsDscConfigDocument -ResourceName 'Microsoft.Windows/Registry' -ResourceInput @{'keyPath' = 'HKCU\1'} -ResourceDescription 'Registry'
 
        Returns:
        {"$schema":"https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json","resources":[{"name":"Registry","type":"Microsoft.Windows/Registry","properties":"@{keyPath=HKCU\\1}"}]}
 
    .EXAMPLE
        PS C:\> Init-PsDscConfigDocument -ResourceName Microsoft.WinGet.DSC/WinGetPackage -IsPwsh -ResourceInput @{ Id = 'Microsoft.PowerShell.Preview'} -ResourceDescription 'WinGetPackage'
 
        Returns:
        {"$schema":"https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json","resources":[{"name":"Using Pwsh","type":"Microsoft.DSC/PowerShell","properties":"@{resources=}"}]}
 
    .OUTPUTS
        System.String
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [Alias('Init-PsDscConfigDocument')]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ArgumentCompleter([DscConfigCompleter])]
        [string]$ResourceName,

        [Parameter(Mandatory = $false)]
        [ArgumentCompleter([DscConfigInputCompleter])]
        [AllowNull()]
        [hashtable]$ResourceInput,

        [Parameter(Mandatory = $false)]
        [string]$ResourceDescription,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]
        $IsPwsh,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]
        $IsWindowsPowerShell

    )

    if ([string]::IsNullOrEmpty($ResourceDescription))
    {
        $ResourceDescription = $ResourceName
    }

    $resources = [ordered]@{
        name       = $ResourceDescription
        type       = $ResourceName
        properties = $ResourceInput
    }

    if ($IsPwsh.IsPresent)
    {
        $resources = [ordered]@{
            name       = 'Using Pwsh'
            type       = 'Microsoft.DSC/PowerShell'
            properties = [ordered]@{
                resources = $resources
            }
        }
    }

    if ($IsWindowsPowerShell.IsPresent)
    {
        $resources = [ordered]@{
            name       = 'Using Pwsh'
            type       = 'Microsoft.Windows/WindowsPowerShell'
            properties = [ordered]@{
                resources = $resources
            }
        }
    }

    $configDocument = [ordered]@{
        '$schema' = 'https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json'
        resources = @($resources)

    }

    return $configDocument | ConvertTo-Json -Depth 10
}
#EndRegion '.\Public\Initialize-PsDscConfigDocument.ps1' 111
#Region '.\Public\Install-DscCLI.ps1' -1

function Install-DscCLI
{
    <#
    .SYNOPSIS
        Install DSC CLI (Windows only).
 
    .DESCRIPTION
        The function Install-DscCLI installs Desired State Configuration version 3 executablle.
 
    .PARAMETER Force
        This switch will force DSC to be installed, even if another installation is already in place.
 
    .PARAMETER Version
        The version of DSC to install.
 
    .PARAMETER UseWinGet
        Use the Windows Package Manager to install DSC.
 
    .PARAMETER UseGitHub
        Use GitHub to install DSC.
 
    .EXAMPLE
        PS C:\> Install-DscCli
 
        Install the latest version of DSC
 
    .EXAMPLE
        PS C:\> Install-DscCli -Force
 
        Install DSC and forces the installed if there is already a version installed.
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param (
        [Parameter()]
        [ArgumentCompleter([DscVersionCompleter])]
        [System.String]
        $Version,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]$UseWinGet,

        [Parameter(Mandatory = $false)]
        [System.Management.Automation.SwitchParameter]$UseGitHub,

        [Parameter()]
        [System.Management.Automation.SwitchParameter]
        $Force
    )
    Write-Verbose -Message ("Starting: {0}" -f $MyInvocation.MyCommand.Name)

    $base = 'https://api.github.com/repos/PowerShell/DSC/releases'

    if ($PSBoundParameters.ContainsKey('Version'))
    {
        $releaseUrl = ('{0}/tags/v{1}' -f $base, $Version)

        $UseVersion = $true
    }
    else
    {
        # TODO: no latest tag because no official release
        $releaseUrl = $base
    }

    $releases = Invoke-RestMethod -Uri $releaseUrl

    # TODO: remove after latest is known
    if ($releases.Count -gt 1)
    {
        $releases = $releases | Sort-Object -Descending | Select-Object -First 1
    }

    if ($IsWindows)
    {
        if ($UseWinGet.IsPresent)
        {
            $winGetModuleInstalled = TestWinGetModule

            if (-not $winGetModuleInstalled)
            {
                throw "This function requires the 'Microsoft.WinGet.Dsc' module to be installed. Please install it using 'Install-PSResource -Name Microsoft.WinGet.Dsc'."
            }

            $hashArgs = @{
                Id          = '9PCX3HX4HZ0Z'
                MatchOption = 'EqualsCaseInsensitive'
            }

            $versionPackage = Get-WinGetPackage @hashArgs

            if (-not $versionPackage)
            {
                $dscInstalled = Install-WinGetPackage @hashArgs

                if ($dscInstalled.Status -eq 'Ok')
                {
                    $version = GetDscVersion
                    Write-Verbose -Message "DSC successfully installed with version $version"

                    # return
                    return $true
                }
            }

            if ($versionPackage)
            {
                $versions = Find-WinGetPackage @hashArgs | Select-Object -First 1 # TODO: validate if multiple version are available

                if ($versionPackage.Version -le $versions.Version -and $PSBoundParameters.ContainsKey('Force'))
                {
                    Write-Verbose -Message "Updating DSC version: '$($versionPackage.Version)' to '$($versions.Version)'"
                    $dscInstalled = Update-WinGetPackage @hashArgs

                    if ($dscInstalled.Status -eq 'Ok' -or $dscInstalled.Status -eq 'NoApplicableUpgrade')
                    {
                        $version = GetDscVersion
                        Write-Verbose -Message "DSC successfully updated with version $version"

                        # return
                        return $true
                    }
                }

                if ($versionPackage.Version -le $versions.Version)
                {
                    Write-Warning -Message "DSC is already installed with version $($versionPackage.Version), but you have not specified the -Force switch to update it. If you want to update it, please add the -Force parameter."

                    # return
                    return $false
                }
                else
                {
                    Write-Verbose -Message "DSC is already installed with version $($versionPackage.Version)."

                    # return
                    return $true
                }
            }
        }

        if ($UseGitHub.IsPresent)
        {
            $fileName = 'DSC-3.0.0-*-x86_64-pc-windows-msvc.zip'
            # get latest asset to be downloaded
            $asset = $releases.assets | Where-Object -Property Name -Like $fileName

            # download the installer
            $tmpdir = [System.IO.Path]::GetTempPath()
            $fileName = $asset.name
            $installerPath = [System.IO.Path]::Combine($tmpDir, $fileName)
            (New-Object Net.WebClient).DownloadFileAsync($asset.browser_download_url, $installerPath)
            Write-Verbose "Downloading $($asset.browser_download_url) to location $installerPath"
            do
            {
                $PercentComplete = [math]::Round((Get-Item $installerPath).Length / $asset.size * 100)
                Write-Progress -Activity 'Downloading DSC' -PercentComplete $PercentComplete
                start-sleep 1
            } while ((Get-Item $installerPath).Length -lt $asset.size)

            # expand the installer to directory
            $elevated = TestAdministrator
            $exePath = if ($elevated)
            {
                Join-Path $env:ProgramFiles 'dsc'
            }
            else
            {
                Join-Path $env:LOCALAPPDATA 'dsc'
            }

            Write-Verbose -Message ("Expanding '{0}' to '{1}'" -f $installerPath, $exePath)
            $null = Expand-Archive -LiteralPath $installerPath -DestinationPath $exePath -Force

            if ($elevated)
            {
                $exePath | AddToPath -Persistent:$true
            }
            else
            {
                $exePath | AddToPath -User
            }

            if (TestDsc)
            {
                $dsc = GetDscVersion
                Write-Verbose -Message "DSC successfully installed with version $dsc"

                return $true
            }
        }
    }
    elseif ($IsLinux)
    {
        if ($UseVersion)
        {
            $filePath = '/tmp/DSC-' + $Version + '-x86_64-unknown-linux-gnu.tar.gz'
            $uri = "https://github.com/PowerShell/DSC/releases/download/v$Version/DSC-$Version-x86_64-unknown-linux-gnu.tar.gz"
        }
        else
        {
            $filePath = '/tmp/DSC-3.0.0-x86_64-unknown-linux-gnu.tar.gz'
            $fileName = 'DSC-3.0.0-*-x86_64-unknown-linux-gnu.tar.gz'
            $uri = ($releases.assets | Where-Object -Property Name -Like $fileName).browser_download_url
        }

        Write-Verbose -Message "Using URI: $uri on path: $filePath"
        curl -L -o $filePath $uri
        # Create the target folder where powershell will be placed
        sudo mkdir -p /opt/microsoft/dsc

        # Expand powershell to the target folder
        sudo tar zxf $filePath -C /opt/microsoft/dsc

        # Set execute permissions
        sudo chmod +x /opt/microsoft/dsc

        # Create the symbolic link that points to pwsh
        sudo ln -s /opt/microsoft/dsc /usr/bin/dsc

        # Add to path
        $env:PATH += [System.IO.Path]::PathSeparator + "/usr/bin/dsc"

        # Call version
        dsc --version
    }
}
#EndRegion '.\Public\Install-DscCLI.ps1' 231
#Region '.\Public\Invoke-PsDscConfig.ps1' -1

function Invoke-PsDscConfig
{
    <#
    .SYNOPSIS
        Invoke DSC version 3 config using the command-line utility
 
    .DESCRIPTION
        The function Invoke-PsDscConfig invokes Desired State Configuration version 3 configuration documents calling 'dsc.exe'.
 
    .PARAMETER ResourceInput
        The resource input to provide. Supports:
 
        - JSON (string and path)
        - YAML (string and path)
        - PowerShell hash table
        - PowerShell configuration document script (.ps1)
 
    .PARAMETER Operation
        The operation capability to execute e.g. 'Set'.
 
    .PARAMETER Parameter
        Optionally, the parameter input to provide.
 
    .EXAMPLE
        PS C:\> Invoke-PsDscConfig -ResourceInput myconfig.dsc.config.yaml -Parameter myconfig.dsc.config.parameters.yaml
 
    .EXAMPLE
        PS C:\> Invoke-PsDscConfig -ResourceInput @{keyPath = 'HKCU\1'} -Operation Set
 
    .EXAMPLE
        PS C:\> $script = @'
        configuration WinGet {
            Import-DscResource -ModuleName 'Microsoft.WinGet.DSC'
 
            node localhost {
                WinGetPackage WinGetPackageExample
                {
                    Id = 'Microsoft.PowerShell.Preview'
                    Ensure = 'Present'
                }
            }
        }
        '@
        PS C:\> Set-Content -Path 'winget.powershell.dsc.config.ps1' -Value $script
        PS C:\> Invoke-PsDscConfig -ResourceInput 'winget.powershell.dsc.config.ps1' -Operation Set
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'Required for down-level function(s).')]
    param
    (
        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [object]
        $ResourceInput,

        [Parameter(Mandatory = $true)]
        [ValidateSet('Get', 'Set', 'Test', 'Export')]
        [System.String]
        $Operation,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [object]
        $Parameter
    )

    begin
    {
        Write-Verbose -Message ("Starting: {0}" -f $MyInvocation.MyCommand.Name)

        $boundParameters = GetBoundParameters -BoundParameters $PSBoundParameters -GoodKeys @('ResourceInput', 'Parameter')

        Write-Verbose ($boundParameters | ConvertTo-Json | Out-String)
    }

    process
    {
        switch ($Operation)
        {
            'Get'
            {
                $inputObject = GetDscConfigCommand @boundParameters
            }
            'Set'
            {
                $inputObject = SetDscConfigCommand @boundParameters
            }
            'Test'
            {
                $inputobject = TestDscConfigCommand @boundParameters
            }
            'Delete'
            {
                $inputobject = RemoveDscConfigCommand @boundParameters
            }
        }
    }

    end
    {
        Write-Verbose ("Ended: {0}" -f $MyInvocation.MyCommand.Name)

        # return
        return $inputObject
    }
}
#EndRegion '.\Public\Invoke-PsDscConfig.ps1' 110
#Region '.\Public\Invoke-PsDscResource.ps1' -1

function Invoke-PsDscResource
{
    <#
    .SYNOPSIS
        Invoke DSC version 3 resource using the command-line utility
 
    .DESCRIPTION
        The function Invoke-PsDscResource invokes Desired State Configuration version 3 resources calling the executable.
 
    .PARAMETER ResourceName
        The resource name to execute.
 
    .PARAMETER Operation
        The operation capability to execute e.g. 'Set'.
 
    .PARAMETER ResourceInput
        The resource input to provide. Supports JSON, YAML path and PowerShell hash table.
 
    .EXAMPLE
        PS C:\> Invoke-PsDscResource -ResourceName Microsoft.Windows/RebootPending -Operation Get
 
        Execute Microsoft.Windows/RebootPending resource on Windows system to check if there is a pending reboot
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'Required for down-level function(s).')]
    param
    (
        [Parameter(Mandatory = $true)]
        [Alias('Name')]
        [ArgumentCompleter([DscResourceCompleter])]
        [System.String]
        $ResourceName,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Get', 'Set', 'Test', 'Delete', 'Export')]
        [System.String]
        $Operation,

        [Parameter(Mandatory = $false)]
        [ArgumentCompleter([DscResourceInputCompleter])]
        [AllowNull()]
        [object]
        $ResourceInput
    )

    begin
    {
        Write-Verbose -Message ("Starting: {0}" -f $MyInvocation.MyCommand.Name)

        # get the bound parameters without common
        $boundParameters = GetBoundParameters -BoundParameters $PSBoundParameters @("ResourceName", "ResourceInput")

        Write-Verbose ($boundParameters | ConvertTo-Json | Out-String)
    }

    process
    {
        if (-not $PSBoundParameters.ContainsKey('Operation') -and $PSBoundParameters.ContainsKey('ResourceInput'))
        {
            $boundParameters.Remove('ResourceInput')
        }

        switch ($Operation)
        {
            'Get'
            {
                $inputObject = GetDscResourceCommand @boundParameters
            }
            'Set'
            {
                $inputObject = SetDscResourceCommand @boundParameters
            }
            'Test'
            {
                $inputobject = TestDscResourceCommand @boundParameters
            }
            'Delete'
            {
                $inputobject = RemoveDscResourceCommand @boundParameters
            }
            'Export'
            {
                $inputobject = ExportDscResourceCommand @boundParameters
            }
            default
            {
                $inputObject = FindDscResourceCommand @boundParameters
            }
        }
    }

    end
    {
        Write-Verbose ("Ended: {0}" -f $MyInvocation.MyCommand.Name)
        $inputObject
    }
}
#EndRegion '.\Public\Invoke-PsDscResource.ps1' 101
#Region '.\Public\New-PsDscVsCodeSettingsFile.ps1' -1

function New-PsDscVsCodeSettingsFile
{
    <#
    .SYNOPSIS
        Simple function to add schema definitions to VSCode settings file.
 
    .DESCRIPTION
        The function New-PsDscVsCodeSettingsFile adds schema definitions to the 'settings.json' file to help author DSC Configuration Documents.
 
    .PARAMETER Path
        The path to the VSCode settings file. Defaults to $Home\AppData\Roaming\Code\User\settings.json
 
    .EXAMPLE
        PS C:\> New-PsDscVsCodeSettingsFile
 
    .EXAMPLE
        PS C:\> New-PsDscVsCodeSettingsFile -Path customsettingsfile.json
 
    .OUTPUTS
        System.String
 
    .NOTES
        For more details, go to module repository at: https://github.com/Gijsreyn/PSDSC.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    [OutputType([System.String])]
    param
    (
        [Parameter(Mandatory = $false)]
        [AllowNull()]
        $Path = "$Home\AppData\Roaming\Code\User\settings.json"
    )

    $schema = "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/config/document.vscode.json"
    $settings = @"
{
    "json.schemas": [
        {
            "fileMatch": ["**/*.dsc.config.json"],
            "url": "$schema"
        }
    ],
    "yaml.schemas": {
        "$schema": "**/*.dsc.config.yaml"
    },
    "yaml.completion": true
}
"@


    $params = @{
        Path     = $Path
        Encoding = 'utf8'
        Value    = $settings
    }

    if (-not (Test-Path $Path -ErrorAction SilentlyContinue))
    {
        Write-Verbose -Message ("Creating new file: '$Path' with")
        Write-Verbose -Message $settings

        Set-Content @params
    }
    else
    {
        try
        {
            $current = Get-Content $Path | ConvertFrom-Json -ErrorAction Stop
            $reference = ($current | ConvertTo-Json | ConvertFrom-Json)

            # schema object
            $yamlObject = [PSCustomObject]@{
                $schema = '**/*.dsc.config.yaml'
            }

            $jsonObject = [PSCustomObject]@{
                fileMatch = @('**/*.dsc.config.json')
                url       = $schema
            }

            # add to current
            $current | Add-Member -NotePropertyName 'yaml.schemas' -TypeName NoteProperty -NotePropertyValue $yamlObject -Force
            $current | Add-Member -NotePropertyName 'json.schemas' -TypeName NoteProperty -NotePropertyValue @($jsonObject) -Force

            $settings = $current | ConvertTo-Json -Depth 10

            Write-Verbose -Message "Previous settings file:"
            Write-Verbose -Message ($reference | ConvertTo-Json -Depth 5 | Out-String)

            $params.Value = $settings

            if ($PSCmdlet.ShouldProcess($Path, 'overwrite'))
            {
                Set-Content @params
            }
        }
        catch
        {
            Throw ("'$Path' is not a valid .JSON file. Error: {0}" -f $PSItem.Exception.Message)
        }
    }

    return $settings
}
#EndRegion '.\Public\New-PsDscVsCodeSettingsFile.ps1' 104